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,1062 @@
|
|
|
1
|
+
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
u'''(INTERNAL) Private C{CartesianBase} class for elliposiodal, spherical and N-/vectorial
|
|
5
|
+
C{Cartesian}s and public functions L{rtp2xyz}, L{rtp2xyz_}, L{xyz2rtp} and L{xyz2rtp_}.
|
|
6
|
+
|
|
7
|
+
After I{(C) Chris Veness 2011-2015} published under the same MIT Licence**, see
|
|
8
|
+
U{https://www.Movable-Type.co.UK/scripts/latlong.html},
|
|
9
|
+
U{https://www.Movable-Type.co.UK/scripts/latlong-vectors.html} and
|
|
10
|
+
U{https://www.Movable-Type.co.UK/scripts/geodesy/docs/latlon-ellipsoidal.js.html}.
|
|
11
|
+
'''
|
|
12
|
+
|
|
13
|
+
# from pygeodesy.basics import _xinstanceof # from .datums
|
|
14
|
+
from pygeodesy.constants import EPS, EPS0, INT0, PI2, _isfinite, isnear0, \
|
|
15
|
+
_0_0, _1_0, _N_1_0, _2_0, _4_0, _6_0
|
|
16
|
+
from pygeodesy.datums import Datum, _earth_ellipsoid, _spherical_datum, \
|
|
17
|
+
Transform, _WGS84, _xinstanceof
|
|
18
|
+
# from pygeodesy.ecef import EcefKarney # _MODS
|
|
19
|
+
from pygeodesy.errors import _IsnotError, _TypeError, _ValueError, \
|
|
20
|
+
_xdatum, _xkwds
|
|
21
|
+
from pygeodesy.fmath import cbrt, hypot, hypot_, hypot2, fabs, sqrt # hypot
|
|
22
|
+
# from pygeodesy.formy import _hartzell # _MODS
|
|
23
|
+
from pygeodesy.fsums import fsumf_, Fmt
|
|
24
|
+
from pygeodesy.interns import NN, _COMMASPACE_, _phi_ # _not_
|
|
25
|
+
from pygeodesy.interns import _ellipsoidal_, _spherical_ # PYCHOK used!
|
|
26
|
+
from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS
|
|
27
|
+
from pygeodesy.named import _NamedTuple, _Pass
|
|
28
|
+
from pygeodesy.namedTuples import LatLon4Tuple, Vector3Tuple, Vector4Tuple, \
|
|
29
|
+
Bearing2Tuple # PYCHOK .sphericalBase
|
|
30
|
+
# from pygeodesy.nvectorBase import _N_vector # _MODS
|
|
31
|
+
from pygeodesy.props import deprecated_method, Property, Property_RO, \
|
|
32
|
+
property_doc_, property_RO, _update_all
|
|
33
|
+
# from pygeodesy.resections import cassini, collins5, pierlot, tienstra7
|
|
34
|
+
# from pygeodesy.streprs import Fmt # from .fsums
|
|
35
|
+
# from pygeodesy.triaxials import Triaxial_ # _MODS
|
|
36
|
+
from pygeodesy.units import Degrees, Height, _heigHt, _isMeter, Meter, \
|
|
37
|
+
Radians, _toDegrees, _toRadians
|
|
38
|
+
from pygeodesy.utily import acos1, sincos2d, sincos2_, atan2, degrees, radians
|
|
39
|
+
from pygeodesy.vector3d import Vector3d, _xyzhdn3
|
|
40
|
+
# from pygeodesy.vector3dBase import _xyz3 # _MODS
|
|
41
|
+
# from pygeodesy import ltp, resections # _MODS
|
|
42
|
+
|
|
43
|
+
# from math import atan2, degrees, fabs, radians, sqrt # from .fmath, .utily
|
|
44
|
+
|
|
45
|
+
__all__ = _ALL_LAZY.cartesianBase
|
|
46
|
+
__version__ = '24.02.22'
|
|
47
|
+
|
|
48
|
+
_r_ = 'r'
|
|
49
|
+
_theta_ = 'theta'
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class CartesianBase(Vector3d):
|
|
53
|
+
'''(INTERNAL) Base class for ellipsoidal and spherical C{Cartesian}.
|
|
54
|
+
'''
|
|
55
|
+
_datum = None # L{Datum}, to be overriden
|
|
56
|
+
_height = None # height (L{Height}), set or approximated
|
|
57
|
+
|
|
58
|
+
def __init__(self, x_xyz, y=None, z=None, datum=None, ll=None, name=NN):
|
|
59
|
+
'''New C{Cartesian...}.
|
|
60
|
+
|
|
61
|
+
@arg x_xyz: Cartesian X coordinate (C{scalar}) or a C{Cartesian},
|
|
62
|
+
L{Ecef9Tuple}, L{Vector3Tuple} or L{Vector4Tuple}.
|
|
63
|
+
@kwarg y: Cartesian Y coordinate (C{scalar}), ignored if B{C{x_xyz}}
|
|
64
|
+
is not C{scalar}, otherwise same units as B{C{x_xyz}}.
|
|
65
|
+
@kwarg z: Cartesian Z coordinate (C{scalar}), ignored if B{C{x_xyz}}
|
|
66
|
+
is not C{scalar}, otherwise same units as B{C{x_xyz}}.
|
|
67
|
+
@kwarg datum: Optional datum (L{Datum}, L{Ellipsoid}, L{Ellipsoid2}
|
|
68
|
+
or L{a_f2Tuple}).
|
|
69
|
+
@kwarg ll: Optional, original latlon (C{LatLon}).
|
|
70
|
+
@kwarg name: Optional name (C{str}).
|
|
71
|
+
|
|
72
|
+
@raise TypeError: Non-scalar B{C{x_xyz}}, B{C{y}} or B{C{z}} coordinate
|
|
73
|
+
or B{C{x_xyz}} not a C{Cartesian}, L{Ecef9Tuple},
|
|
74
|
+
L{Vector3Tuple} or L{Vector4Tuple} or B{C{datum}} is
|
|
75
|
+
not a L{Datum}.
|
|
76
|
+
'''
|
|
77
|
+
h, d, n = _xyzhdn3(x_xyz, None, datum, ll)
|
|
78
|
+
Vector3d.__init__(self, x_xyz, y=y, z=z, ll=ll, name=name or n)
|
|
79
|
+
if h is not None:
|
|
80
|
+
self._height = Height(h)
|
|
81
|
+
if d is not None:
|
|
82
|
+
self.datum = d
|
|
83
|
+
|
|
84
|
+
# def __matmul__(self, other): # PYCHOK Python 3.5+
|
|
85
|
+
# '''Return C{NotImplemented} for C{c_ = c @ datum} and C{c_ = c @ transform}.
|
|
86
|
+
# '''
|
|
87
|
+
# return NotImplemented if isinstance(other, (Datum, Transform)) else \
|
|
88
|
+
# _NotImplemented(self, other)
|
|
89
|
+
|
|
90
|
+
def cassini(self, pointB, pointC, alpha, beta, useZ=False):
|
|
91
|
+
'''3-Point resection between this and 2 other points using U{Cassini
|
|
92
|
+
<https://NL.WikiPedia.org/wiki/Achterwaartse_insnijding>}'s method.
|
|
93
|
+
|
|
94
|
+
@arg pointB: Second point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple},
|
|
95
|
+
C{Vector4Tuple} or C{Vector2Tuple} if C{B{useZ}=False}).
|
|
96
|
+
@arg pointC: Center point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple},
|
|
97
|
+
C{Vector4Tuple} or C{Vector2Tuple} if C{B{useZ}=False}).
|
|
98
|
+
@arg alpha: Angle subtended by triangle side C{b} from B{C{pointA}} to
|
|
99
|
+
B{C{pointC}} (C{degrees}, non-negative).
|
|
100
|
+
@arg beta: Angle subtended by triangle side C{a} from B{C{pointB}} to
|
|
101
|
+
B{C{pointC}} (C{degrees}, non-negative).
|
|
102
|
+
@kwarg useZ: If C{True}, use and interpolate the Z component, otherwise
|
|
103
|
+
force C{z=INT0} (C{bool}).
|
|
104
|
+
|
|
105
|
+
@note: Typically, B{C{pointC}} is between this and B{C{pointB}}.
|
|
106
|
+
|
|
107
|
+
@return: The survey point, an instance of this (sub-)class.
|
|
108
|
+
|
|
109
|
+
@raise ResectionError: Near-coincident, -colinear or -concyclic points
|
|
110
|
+
or negative or invalid B{C{alpha}} or B{C{beta}}.
|
|
111
|
+
|
|
112
|
+
@raise TypeError: Invalid B{C{pointA}}, B{C{pointB}} or B{C{pointM}}.
|
|
113
|
+
|
|
114
|
+
@see: Function L{pygeodesy.cassini} for references and more details.
|
|
115
|
+
'''
|
|
116
|
+
return self._resections.cassini(self, pointB, pointC, alpha, beta,
|
|
117
|
+
useZ=useZ, datum=self.datum)
|
|
118
|
+
|
|
119
|
+
@deprecated_method
|
|
120
|
+
def collins(self, pointB, pointC, alpha, beta, useZ=False):
|
|
121
|
+
'''DEPRECATED, use method L{collins5}.'''
|
|
122
|
+
return self.collins5(pointB, pointC, alpha, beta, useZ=useZ)
|
|
123
|
+
|
|
124
|
+
def collins5(self, pointB, pointC, alpha, beta, useZ=False):
|
|
125
|
+
'''3-Point resection between this and 2 other points using U{Collins<https://Dokumen.tips/
|
|
126
|
+
documents/three-point-resection-problem-introduction-kaestner-burkhardt-method.html>}' method.
|
|
127
|
+
|
|
128
|
+
@arg pointB: Second point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple},
|
|
129
|
+
C{Vector4Tuple} or C{Vector2Tuple} if C{B{useZ}=False}).
|
|
130
|
+
@arg pointC: Center point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple},
|
|
131
|
+
C{Vector4Tuple} or C{Vector2Tuple} if C{B{useZ}=False}).
|
|
132
|
+
@arg alpha: Angle subtended by triangle side C{b} from B{C{pointA}} to
|
|
133
|
+
B{C{pointC}} (C{degrees}, non-negative).
|
|
134
|
+
@arg beta: Angle subtended by triangle side C{a} from B{C{pointB}} to
|
|
135
|
+
B{C{pointC}} (C{degrees}, non-negative).
|
|
136
|
+
@kwarg useZ: If C{True}, use and interpolate the Z component, otherwise
|
|
137
|
+
force C{z=INT0} (C{bool}).
|
|
138
|
+
|
|
139
|
+
@note: Typically, B{C{pointC}} is between this and B{C{pointB}}.
|
|
140
|
+
|
|
141
|
+
@return: L{Collins5Tuple}C{(pointP, pointH, a, b, c)} with survey C{pointP},
|
|
142
|
+
auxiliary C{pointH}, each an instance of this (sub-)class and
|
|
143
|
+
triangle sides C{a}, C{b} and C{c}.
|
|
144
|
+
|
|
145
|
+
@raise ResectionError: Near-coincident, -colinear or -concyclic points
|
|
146
|
+
or negative or invalid B{C{alpha}} or B{C{beta}}.
|
|
147
|
+
|
|
148
|
+
@raise TypeError: Invalid B{C{pointB}} or B{C{pointM}}.
|
|
149
|
+
|
|
150
|
+
@see: Function L{pygeodesy.collins5} for references and more details.
|
|
151
|
+
'''
|
|
152
|
+
return self._resections.collins5(self, pointB, pointC, alpha, beta,
|
|
153
|
+
useZ=useZ, datum=self.datum)
|
|
154
|
+
|
|
155
|
+
@deprecated_method
|
|
156
|
+
def convertDatum(self, datum2, **datum):
|
|
157
|
+
'''DEPRECATED, use method L{toDatum}.'''
|
|
158
|
+
return self.toDatum(datum2, **datum)
|
|
159
|
+
|
|
160
|
+
@property_doc_(''' this cartesian's datum (L{Datum}).''')
|
|
161
|
+
def datum(self):
|
|
162
|
+
'''Get this cartesian's datum (L{Datum}).
|
|
163
|
+
'''
|
|
164
|
+
return self._datum
|
|
165
|
+
|
|
166
|
+
@datum.setter # PYCHOK setter!
|
|
167
|
+
def datum(self, datum):
|
|
168
|
+
'''Set this cartesian's C{datum} I{without conversion}
|
|
169
|
+
(L{Datum}), ellipsoidal or spherical.
|
|
170
|
+
|
|
171
|
+
@raise TypeError: The B{C{datum}} is not a L{Datum}.
|
|
172
|
+
'''
|
|
173
|
+
d = _spherical_datum(datum, name=self.name)
|
|
174
|
+
if self._datum: # is not None
|
|
175
|
+
if d.isEllipsoidal and not self._datum.isEllipsoidal:
|
|
176
|
+
raise _IsnotError(_ellipsoidal_, datum=datum)
|
|
177
|
+
elif d.isSpherical and not self._datum.isSpherical:
|
|
178
|
+
raise _IsnotError(_spherical_, datum=datum)
|
|
179
|
+
if self._datum != d:
|
|
180
|
+
_update_all(self)
|
|
181
|
+
self._datum = d
|
|
182
|
+
|
|
183
|
+
def destinationXyz(self, delta, Cartesian=None, **Cartesian_kwds):
|
|
184
|
+
'''Calculate the destination using a I{local} delta from this cartesian.
|
|
185
|
+
|
|
186
|
+
@arg delta: Local delta to the destination (L{XyzLocal}, L{Enu},
|
|
187
|
+
L{Ned} or L{Local9Tuple}).
|
|
188
|
+
@kwarg Cartesian: Optional (geocentric) class to return the
|
|
189
|
+
destination or C{None}.
|
|
190
|
+
@kwarg Cartesian_kwds: Optional, additional B{C{Cartesian}} keyword
|
|
191
|
+
arguments, ignored if C{B{Cartesian} is None}.
|
|
192
|
+
|
|
193
|
+
@return: Destination as a C{B{Cartesian}(x, y, z, **B{Cartesian_kwds})}
|
|
194
|
+
instance or if C{B{Cartesian} is None}, an L{Ecef9Tuple}C{(x, y,
|
|
195
|
+
z, lat, lon, height, C, M, datum)} with C{M=None} always.
|
|
196
|
+
|
|
197
|
+
@raise TypeError: Invalid B{C{delta}}, B{C{Cartesian}} or
|
|
198
|
+
B{C{Cartesian_kwds}}.
|
|
199
|
+
'''
|
|
200
|
+
if Cartesian is None:
|
|
201
|
+
r = self._Ltp._local2ecef(delta, nine=True)
|
|
202
|
+
else:
|
|
203
|
+
r = self._Ltp._local2ecef(delta, nine=False)
|
|
204
|
+
r = Cartesian(*r, **_xkwds(Cartesian_kwds, datum=self.datum))
|
|
205
|
+
return r._xnamed(r) if self.name else r
|
|
206
|
+
|
|
207
|
+
@property_RO
|
|
208
|
+
def Ecef(self):
|
|
209
|
+
'''Get the ECEF I{class} (L{EcefKarney}), I{once}.
|
|
210
|
+
'''
|
|
211
|
+
CartesianBase.Ecef = E = _MODS.ecef.EcefKarney # overwrite property_RO
|
|
212
|
+
return E
|
|
213
|
+
|
|
214
|
+
@Property_RO
|
|
215
|
+
def _ecef9(self):
|
|
216
|
+
'''(INTERNAL) Helper for L{toEcef}, L{toLocal} and L{toLtp} (L{Ecef9Tuple}).
|
|
217
|
+
'''
|
|
218
|
+
return self.Ecef(self.datum, name=self.name).reverse(self, M=True)
|
|
219
|
+
|
|
220
|
+
@property_RO
|
|
221
|
+
def ellipsoidalCartesian(self):
|
|
222
|
+
'''Get the C{Cartesian type} iff ellipsoidal, overloaded in L{CartesianEllipsoidalBase}.
|
|
223
|
+
'''
|
|
224
|
+
return False
|
|
225
|
+
|
|
226
|
+
def hartzell(self, los=False, earth=None):
|
|
227
|
+
'''Compute the intersection of a Line-Of-Sight from this cartesian Point-Of-View
|
|
228
|
+
(pov) and this cartesian's ellipsoid surface.
|
|
229
|
+
|
|
230
|
+
@kwarg los: Line-Of-Sight, I{direction} to the ellipsoid (L{Los}, L{Vector3d}),
|
|
231
|
+
C{True} for the I{normal, plumb} onto the surface or I{False} or
|
|
232
|
+
C{None} to point to the center of the ellipsoid.
|
|
233
|
+
@kwarg earth: The earth model (L{Datum}, L{Ellipsoid}, L{Ellipsoid2}, L{a_f2Tuple}
|
|
234
|
+
or C{scalar} radius in C{meter}), overriding this cartesian's
|
|
235
|
+
C{datum} ellipsoid.
|
|
236
|
+
|
|
237
|
+
@return: The intersection (C{Cartesian}) with C{.height} set to the distance to
|
|
238
|
+
this C{pov}.
|
|
239
|
+
|
|
240
|
+
@raise IntersectionError: Null or bad C{pov} or B{C{los}}, this C{pov} is inside
|
|
241
|
+
the ellipsoid or B{C{los}} points outside or away from
|
|
242
|
+
the ellipsoid.
|
|
243
|
+
|
|
244
|
+
@raise TypeError: Invalid B{C{los}} or invalid or undefined B{C{earth}} or C{datum}.
|
|
245
|
+
|
|
246
|
+
@see: Function L{hartzell<pygeodesy.formy.hartzell>} for further details.
|
|
247
|
+
'''
|
|
248
|
+
return _MODS.formy._hartzell(self, los, earth)
|
|
249
|
+
|
|
250
|
+
@Property
|
|
251
|
+
def height(self):
|
|
252
|
+
'''Get the height (C{meter}).
|
|
253
|
+
'''
|
|
254
|
+
return self._height4.h if self._height is None else self._height
|
|
255
|
+
|
|
256
|
+
@height.setter # PYCHOK setter!
|
|
257
|
+
def height(self, height):
|
|
258
|
+
'''Set the height (C{meter}).
|
|
259
|
+
|
|
260
|
+
@raise TypeError: Invalid B{C{height}} C{type}.
|
|
261
|
+
|
|
262
|
+
@raise ValueError: Invalid B{C{height}}.
|
|
263
|
+
'''
|
|
264
|
+
h = Height(height)
|
|
265
|
+
if self._height != h:
|
|
266
|
+
_update_all(self)
|
|
267
|
+
self._height = h
|
|
268
|
+
|
|
269
|
+
def _height2C(self, r, Cartesian=None, datum=None, height=INT0, **kwds):
|
|
270
|
+
'''(INTERNAL) Helper for methods C{.height3} and C{.height4}.
|
|
271
|
+
'''
|
|
272
|
+
if Cartesian is not None:
|
|
273
|
+
r = Cartesian(r, **kwds)
|
|
274
|
+
if datum is not None:
|
|
275
|
+
r.datum = datum
|
|
276
|
+
if height is not None:
|
|
277
|
+
r.height = height # Height(height)
|
|
278
|
+
return r
|
|
279
|
+
|
|
280
|
+
def height3(self, earth=None, height=None, **Cartesian_and_kwds):
|
|
281
|
+
'''Compute the cartesian at a height above or below this certesian's ellipsoid.
|
|
282
|
+
|
|
283
|
+
@kwarg earth: A datum, ellipsoid, triaxial ellipsoid or earth radius,
|
|
284
|
+
I{overriding} this cartesian's datum (L{Datum}, L{Ellipsoid},
|
|
285
|
+
L{Ellipsoid2}, L{a_f2Tuple} or C{meter}, conventionally).
|
|
286
|
+
@kwarg height: The height (C{meter}, conventionally), overriding this
|
|
287
|
+
cartesian's height.
|
|
288
|
+
@kwarg Cartesian_and_kwds: Optional C{B{Cartesian}=None} class to return
|
|
289
|
+
the cartesian I{at height} and additional B{C{Cartesian}}
|
|
290
|
+
keyword arguments.
|
|
291
|
+
|
|
292
|
+
@return: An instance of B{C{Cartesian}} or if C{B{Cartesian} is None},
|
|
293
|
+
a L{Vector3Tuple}C{(x, y, z)} with the C{x}, C{y} and C{z}
|
|
294
|
+
coordinates I{at height} in C{meter}, conventionally.
|
|
295
|
+
|
|
296
|
+
@note: This cartesian's coordinates are returned if B{C{earth}} and this
|
|
297
|
+
datum or B{C{heigth}} and/or this height are C{None} or undefined.
|
|
298
|
+
|
|
299
|
+
@note: Include keyword argument C{B{datum}=None} if class B{C{Cartesian}}
|
|
300
|
+
does not accept a B{C{datum}} keyword agument.
|
|
301
|
+
|
|
302
|
+
@raise TriaxialError: No convergence in triaxial root finding.
|
|
303
|
+
|
|
304
|
+
@raise TypeError: Invalid or undefined B{C{earth}} or C{datum}.
|
|
305
|
+
'''
|
|
306
|
+
n = self.height3.__name__
|
|
307
|
+
d = self.datum if earth is None else _spherical_datum(earth, name=n)
|
|
308
|
+
c, h = self, _heigHt(self, height)
|
|
309
|
+
if h and d:
|
|
310
|
+
R, r = self.Roc2(earth=d)
|
|
311
|
+
if R > EPS0:
|
|
312
|
+
R = (R + h) / R
|
|
313
|
+
r = ((r + h) / r) if r > EPS0 else _1_0
|
|
314
|
+
c = c.times_(R, R, r)
|
|
315
|
+
|
|
316
|
+
r = Vector3Tuple(c.x, c.y, c.z, name=n)
|
|
317
|
+
if Cartesian_and_kwds:
|
|
318
|
+
r = self._height2C(r, **_xkwds(Cartesian_and_kwds, datum=d))
|
|
319
|
+
return r
|
|
320
|
+
|
|
321
|
+
@Property_RO
|
|
322
|
+
def _height4(self):
|
|
323
|
+
'''(INTERNAL) Get this C{height4}-tuple.
|
|
324
|
+
'''
|
|
325
|
+
try:
|
|
326
|
+
r = self.datum.ellipsoid.height4(self, normal=True)
|
|
327
|
+
except (AttributeError, ValueError): # no datum, null cartesian,
|
|
328
|
+
r = Vector4Tuple(self.x, self.y, self.z, 0, name=self.height4.__name__)
|
|
329
|
+
return r
|
|
330
|
+
|
|
331
|
+
def height4(self, earth=None, normal=True, **Cartesian_and_kwds):
|
|
332
|
+
'''Compute the projection of this point on and the height above or below
|
|
333
|
+
this datum's ellipsoid surface.
|
|
334
|
+
|
|
335
|
+
@kwarg earth: A datum, ellipsoid, triaxial ellipsoid or earth radius,
|
|
336
|
+
I{overriding} this datum (L{Datum}, L{Ellipsoid},
|
|
337
|
+
L{Ellipsoid2}, L{a_f2Tuple}, L{Triaxial}, L{Triaxial_},
|
|
338
|
+
L{JacobiConformal} or C{meter}, conventionally).
|
|
339
|
+
@kwarg normal: If C{True} the projection is the nearest point on the
|
|
340
|
+
ellipsoid's surface, otherwise the intersection of the
|
|
341
|
+
radial line to the ellipsoid's center and the surface.
|
|
342
|
+
@kwarg Cartesian_and_kwds: Optional C{B{Cartesian}=None} class to return
|
|
343
|
+
the I{projection} and additional B{C{Cartesian}} keyword
|
|
344
|
+
arguments.
|
|
345
|
+
|
|
346
|
+
@return: An instance of B{C{Cartesian}} or if C{B{Cartesian} is None}, a
|
|
347
|
+
L{Vector4Tuple}C{(x, y, z, h)} with the I{projection} C{x}, C{y}
|
|
348
|
+
and C{z} coordinates and height C{h} in C{meter}, conventionally.
|
|
349
|
+
|
|
350
|
+
@note: Include keyword argument C{B{datum}=None} if class B{C{Cartesian}}
|
|
351
|
+
does not accept a B{C{datum}} keyword agument.
|
|
352
|
+
|
|
353
|
+
@raise TriaxialError: No convergence in triaxial root finding.
|
|
354
|
+
|
|
355
|
+
@raise TypeError: Invalid or undefined B{C{earth}} or C{datum}.
|
|
356
|
+
|
|
357
|
+
@see: Methods L{Ellipsoid.height4} and L{Triaxial_.height4} for more information.
|
|
358
|
+
'''
|
|
359
|
+
n = self.height4.__name__
|
|
360
|
+
d = self.datum if earth is None else earth
|
|
361
|
+
if normal and d is self.datum:
|
|
362
|
+
r = self._height4
|
|
363
|
+
elif isinstance(d, _MODS.triaxials.Triaxial_):
|
|
364
|
+
r = d.height4(self, normal=normal)
|
|
365
|
+
try:
|
|
366
|
+
d = d.toEllipsoid(name=n)
|
|
367
|
+
except (TypeError, ValueError): # TriaxialError
|
|
368
|
+
d = None
|
|
369
|
+
else:
|
|
370
|
+
r = _earth_ellipsoid(d).height4(self, normal=normal)
|
|
371
|
+
|
|
372
|
+
if Cartesian_and_kwds:
|
|
373
|
+
if d and not isinstance(d, Datum):
|
|
374
|
+
d = _spherical_datum(d, name=n)
|
|
375
|
+
r = self._height2C(r, **_xkwds(Cartesian_and_kwds, datum=d))
|
|
376
|
+
return r
|
|
377
|
+
|
|
378
|
+
@Property_RO
|
|
379
|
+
def isEllipsoidal(self):
|
|
380
|
+
'''Check whether this cartesian is ellipsoidal (C{bool} or C{None} if unknown).
|
|
381
|
+
'''
|
|
382
|
+
return self.datum.isEllipsoidal if self._datum else None
|
|
383
|
+
|
|
384
|
+
@Property_RO
|
|
385
|
+
def isSpherical(self):
|
|
386
|
+
'''Check whether this cartesian is spherical (C{bool} or C{None} if unknown).
|
|
387
|
+
'''
|
|
388
|
+
return self.datum.isSpherical if self._datum else None
|
|
389
|
+
|
|
390
|
+
@Property_RO
|
|
391
|
+
def latlon(self):
|
|
392
|
+
'''Get this cartesian's (geodetic) lat- and longitude in C{degrees} (L{LatLon2Tuple}C{(lat, lon)}).
|
|
393
|
+
'''
|
|
394
|
+
return self.toEcef().latlon
|
|
395
|
+
|
|
396
|
+
@Property_RO
|
|
397
|
+
def latlonheight(self):
|
|
398
|
+
'''Get this cartesian's (geodetic) lat-, longitude in C{degrees} with height (L{LatLon3Tuple}C{(lat, lon, height)}).
|
|
399
|
+
'''
|
|
400
|
+
return self.toEcef().latlonheight
|
|
401
|
+
|
|
402
|
+
@Property_RO
|
|
403
|
+
def latlonheightdatum(self):
|
|
404
|
+
'''Get this cartesian's (geodetic) lat-, longitude in C{degrees} with height and datum (L{LatLon4Tuple}C{(lat, lon, height, datum)}).
|
|
405
|
+
'''
|
|
406
|
+
return self.toEcef().latlonheightdatum
|
|
407
|
+
|
|
408
|
+
@property_RO
|
|
409
|
+
def _ltp(self):
|
|
410
|
+
'''(INTERNAL) Get module C{.ltp}, I{once}.
|
|
411
|
+
'''
|
|
412
|
+
CartesianBase._ltp = m = _MODS.ltp # overwrite property_RO
|
|
413
|
+
return m
|
|
414
|
+
|
|
415
|
+
@Property_RO
|
|
416
|
+
def _Ltp(self):
|
|
417
|
+
'''(INTERNAL) Cache for L{toLtp}.
|
|
418
|
+
'''
|
|
419
|
+
return self._ltp.Ltp(self._ecef9, ecef=self.Ecef(self.datum), name=self.name)
|
|
420
|
+
|
|
421
|
+
@Property_RO
|
|
422
|
+
def _N_vector(self):
|
|
423
|
+
'''(INTERNAL) Get the (C{nvectorBase._N_vector_}).
|
|
424
|
+
'''
|
|
425
|
+
m = _MODS.nvectorBase
|
|
426
|
+
x, y, z, h = self._n_xyzh4(self.datum)
|
|
427
|
+
return m._N_vector_(x, y, z, h=h, name=self.name)
|
|
428
|
+
|
|
429
|
+
def _n_xyzh4(self, datum):
|
|
430
|
+
'''(INTERNAL) Get the n-vector components as L{Vector4Tuple}.
|
|
431
|
+
'''
|
|
432
|
+
def _ErrorEPS0(x):
|
|
433
|
+
return _ValueError(origin=self, txt=Fmt.PARENSPACED(EPS0=x))
|
|
434
|
+
|
|
435
|
+
_xinstanceof(Datum, datum=datum)
|
|
436
|
+
# <https://www.Movable-Type.co.UK/scripts/geodesy/docs/
|
|
437
|
+
# latlon-nvector-ellipsoidal.js.html#line309>,
|
|
438
|
+
# <https://GitHub.com/pbrod/nvector>/src/nvector/core.py>
|
|
439
|
+
# _equation23 and <https://www.NavLab.net/nvector>
|
|
440
|
+
E = datum.ellipsoid
|
|
441
|
+
x, y, z = self.xyz
|
|
442
|
+
|
|
443
|
+
# Kenneth Gade eqn 23
|
|
444
|
+
p = hypot2(x, y) * E.a2_
|
|
445
|
+
q = z**2 * E.e21 * E.a2_
|
|
446
|
+
r = fsumf_(p, q, -E.e4) / _6_0
|
|
447
|
+
s = (p * q * E.e4) / (_4_0 * r**3)
|
|
448
|
+
t = cbrt(fsumf_(_1_0, s, sqrt(s * (_2_0 + s))))
|
|
449
|
+
if isnear0(t):
|
|
450
|
+
raise _ErrorEPS0(t)
|
|
451
|
+
u = fsumf_(_1_0, t, _1_0 / t) * r
|
|
452
|
+
v = sqrt(u**2 + E.e4 * q)
|
|
453
|
+
t = v * _2_0
|
|
454
|
+
if t < EPS0: # isnear0
|
|
455
|
+
raise _ErrorEPS0(t)
|
|
456
|
+
w = fsumf_(u, v, -q) * E.e2 / t
|
|
457
|
+
k = sqrt(fsumf_(u, v, w**2)) - w
|
|
458
|
+
if isnear0(k):
|
|
459
|
+
raise _ErrorEPS0(k)
|
|
460
|
+
t = k + E.e2
|
|
461
|
+
if isnear0(t):
|
|
462
|
+
raise _ErrorEPS0(t)
|
|
463
|
+
e = k / t
|
|
464
|
+
# d = e * hypot(x, y)
|
|
465
|
+
# tmp = 1 / hypot(d, z) == 1 / hypot(e * hypot(x, y), z)
|
|
466
|
+
t = hypot_(x * e, y * e, z) # == 1 / tmp
|
|
467
|
+
if t < EPS0: # isnear0
|
|
468
|
+
raise _ErrorEPS0(t)
|
|
469
|
+
h = fsumf_(k, E.e2, _N_1_0) / k * t
|
|
470
|
+
s = e / t # == e * tmp
|
|
471
|
+
return Vector4Tuple(x * s, y * s, z / t, h, name=self.name)
|
|
472
|
+
|
|
473
|
+
@Property_RO
|
|
474
|
+
def philam(self):
|
|
475
|
+
'''Get this cartesian's (geodetic) lat- and longitude in C{radians} (L{PhiLam2Tuple}C{(phi, lam)}).
|
|
476
|
+
'''
|
|
477
|
+
return self.toEcef().philam
|
|
478
|
+
|
|
479
|
+
@Property_RO
|
|
480
|
+
def philamheight(self):
|
|
481
|
+
'''Get this cartesian's (geodetic) lat-, longitude in C{radians} with height (L{PhiLam3Tuple}C{(phi, lam, height)}).
|
|
482
|
+
'''
|
|
483
|
+
return self.toEcef().philamheight
|
|
484
|
+
|
|
485
|
+
@Property_RO
|
|
486
|
+
def philamheightdatum(self):
|
|
487
|
+
'''Get this cartesian's (geodetic) lat-, longitude in C{radians} with height and datum (L{PhiLam4Tuple}C{(phi, lam, height, datum)}).
|
|
488
|
+
'''
|
|
489
|
+
return self.toEcef().philamheightdatum
|
|
490
|
+
|
|
491
|
+
def pierlot(self, point2, point3, alpha12, alpha23, useZ=False, eps=EPS):
|
|
492
|
+
'''3-Point resection between this and two other points using U{Pierlot
|
|
493
|
+
<http://www.Telecom.ULg.ac.Be/triangulation>}'s method C{ToTal} with
|
|
494
|
+
I{approximate} limits for the (pseudo-)singularities.
|
|
495
|
+
|
|
496
|
+
@arg point2: Second point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple},
|
|
497
|
+
C{Vector4Tuple} or C{Vector2Tuple} if C{B{useZ}=False}).
|
|
498
|
+
@arg point3: Third point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple},
|
|
499
|
+
C{Vector4Tuple} or C{Vector2Tuple} if C{B{useZ}=False}).
|
|
500
|
+
@arg alpha12: Angle subtended from this point to B{C{point2}} or
|
|
501
|
+
B{C{alpha2 - alpha}} (C{degrees}).
|
|
502
|
+
@arg alpha23: Angle subtended from B{C{point2}} to B{C{point3}} or
|
|
503
|
+
B{C{alpha3 - alpha2}} (C{degrees}).
|
|
504
|
+
@kwarg useZ: If C{True}, interpolate the Z component, otherwise use C{z=INT0}
|
|
505
|
+
(C{bool}).
|
|
506
|
+
@kwarg eps: Tolerance for C{cot} (pseudo-)singularities (C{float}).
|
|
507
|
+
|
|
508
|
+
@note: This point, B{C{point2}} and B{C{point3}} are ordered counter-clockwise.
|
|
509
|
+
|
|
510
|
+
@return: The survey (or robot) point, an instance of this (sub-)class.
|
|
511
|
+
|
|
512
|
+
@raise ResectionError: Near-coincident, -colinear or -concyclic points
|
|
513
|
+
or invalid B{C{alpha12}} or B{C{alpha23}}.
|
|
514
|
+
|
|
515
|
+
@raise TypeError: Invalid B{C{point2}} or B{C{point3}}.
|
|
516
|
+
|
|
517
|
+
@see: Function L{pygeodesy.pierlot} for references and more details.
|
|
518
|
+
'''
|
|
519
|
+
return self._resections.pierlot(self, point2, point3, alpha12, alpha23,
|
|
520
|
+
useZ=useZ, eps=eps, datum=self.datum)
|
|
521
|
+
|
|
522
|
+
def pierlotx(self, point2, point3, alpha1, alpha2, alpha3, useZ=False):
|
|
523
|
+
'''3-Point resection between this and two other points using U{Pierlot
|
|
524
|
+
<http://www.Telecom.ULg.ac.Be/publi/publications/pierlot/Pierlot2014ANewThree>}'s
|
|
525
|
+
method C{ToTal} with I{exact} limits for the (pseudo-)singularities.
|
|
526
|
+
|
|
527
|
+
@arg point2: Second point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple},
|
|
528
|
+
C{Vector4Tuple} or C{Vector2Tuple} if C{B{useZ}=False}).
|
|
529
|
+
@arg point3: Third point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple},
|
|
530
|
+
C{Vector4Tuple} or C{Vector2Tuple} if C{B{useZ}=False}).
|
|
531
|
+
@arg alpha1: Angle at B{C{point1}} (C{degrees}).
|
|
532
|
+
@arg alpha2: Angle at B{C{point2}} (C{degrees}).
|
|
533
|
+
@arg alpha3: Angle at B{C{point3}} (C{degrees}).
|
|
534
|
+
@kwarg useZ: If C{True}, interpolate the survey point's Z component,
|
|
535
|
+
otherwise use C{z=INT0} (C{bool}).
|
|
536
|
+
|
|
537
|
+
@return: The survey (or robot) point, an instance of this (sub-)class.
|
|
538
|
+
|
|
539
|
+
@raise ResectionError: Near-coincident, -colinear or -concyclic points or
|
|
540
|
+
invalid B{C{alpha1}}, B{C{alpha2}} or B{C{alpha3}}.
|
|
541
|
+
|
|
542
|
+
@raise TypeError: Invalid B{C{point2}} or B{C{point3}}.
|
|
543
|
+
|
|
544
|
+
@see: Function L{pygeodesy.pierlotx} for references and more details.
|
|
545
|
+
'''
|
|
546
|
+
return self._resections.pierlotx(self, point2, point3, alpha1, alpha2, alpha3,
|
|
547
|
+
useZ=useZ, datum=self.datum)
|
|
548
|
+
|
|
549
|
+
@property_RO
|
|
550
|
+
def _resections(self):
|
|
551
|
+
'''(INTERNAL) Import the C{.resections} module, I{once}.
|
|
552
|
+
'''
|
|
553
|
+
CartesianBase._resections = m = _MODS.resections # overwrite property_RO
|
|
554
|
+
return m
|
|
555
|
+
|
|
556
|
+
def Roc2(self, earth=None):
|
|
557
|
+
'''Compute this cartesian's I{normal} and I{pseudo, z-based} radius of curvature.
|
|
558
|
+
|
|
559
|
+
@kwarg earth: A datum, ellipsoid, triaxial ellipsoid or earth radius,
|
|
560
|
+
I{overriding} this cartesian's datum (L{Datum}, L{Ellipsoid},
|
|
561
|
+
L{Ellipsoid2}, L{a_f2Tuple} or C{meter}, conventionally).
|
|
562
|
+
|
|
563
|
+
@return: 2-Tuple C{(R, r)} with the I{normal} and I{pseudo, z-based} radius of
|
|
564
|
+
curvature C{R} respectively C{r}, both in C{meter} conventionally.
|
|
565
|
+
|
|
566
|
+
@raise TypeError: Invalid or undefined B{C{earth}} or C{datum}.
|
|
567
|
+
'''
|
|
568
|
+
r = z = fabs( self.z)
|
|
569
|
+
R, _0 = hypot(self.x, self.y), EPS0
|
|
570
|
+
if R < _0: # polar
|
|
571
|
+
R = z
|
|
572
|
+
elif z > _0: # non-equatorial
|
|
573
|
+
d = self.datum if earth is None else _spherical_datum(earth)
|
|
574
|
+
e = self.toLatLon(datum=d, height=0, LatLon=None) # Ecef9Tuple
|
|
575
|
+
M = e.M # EcefMatrix
|
|
576
|
+
sa, ca = map(fabs, (M._2_2_, M._2_1_) if M else sincos2d(e.lat))
|
|
577
|
+
if ca < _0: # polar
|
|
578
|
+
R = z
|
|
579
|
+
else: # prime-vertical, normal roc R
|
|
580
|
+
R = R / ca # /= chokes PyChecker
|
|
581
|
+
r = R if sa < _0 else (r / sa) # non-/equatorial
|
|
582
|
+
return R, r
|
|
583
|
+
|
|
584
|
+
@property_RO
|
|
585
|
+
def sphericalCartesian(self):
|
|
586
|
+
'''Get the C{Cartesian type} iff spherical, overloaded in L{CartesianSphericalBase}.
|
|
587
|
+
'''
|
|
588
|
+
return False
|
|
589
|
+
|
|
590
|
+
@deprecated_method
|
|
591
|
+
def tienstra(self, pointB, pointC, alpha, beta=None, gamma=None, useZ=False):
|
|
592
|
+
'''DEPRECATED, use method L{tienstra7}.'''
|
|
593
|
+
return self.tienstra7(pointB, pointC, alpha, beta=beta, gamma=gamma, useZ=useZ)
|
|
594
|
+
|
|
595
|
+
def tienstra7(self, pointB, pointC, alpha, beta=None, gamma=None, useZ=False):
|
|
596
|
+
'''3-Point resection between this and two other points using U{Tienstra
|
|
597
|
+
<https://WikiPedia.org/wiki/Tienstra_formula>}'s formula.
|
|
598
|
+
|
|
599
|
+
@arg pointB: Second point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple}, C{Vector4Tuple} or
|
|
600
|
+
C{Vector2Tuple} if C{B{useZ}=False}).
|
|
601
|
+
@arg pointC: Third point (C{Cartesian}, L{Vector3d}, C{Vector3Tuple}, C{Vector4Tuple} or
|
|
602
|
+
C{Vector2Tuple} if C{B{useZ}=False}).
|
|
603
|
+
@arg alpha: Angle subtended by triangle side C{a} from B{C{pointB}} to B{C{pointC}} (C{degrees},
|
|
604
|
+
non-negative).
|
|
605
|
+
@kwarg beta: Angle subtended by triangle side C{b} from this to B{C{pointC}} (C{degrees},
|
|
606
|
+
non-negative) or C{None} if C{B{gamma} is not None}.
|
|
607
|
+
@kwarg gamma: Angle subtended by triangle side C{c} from this to B{C{pointB}} (C{degrees},
|
|
608
|
+
non-negative) or C{None} if C{B{beta} is not None}.
|
|
609
|
+
@kwarg useZ: If C{True}, use and interpolate the Z component, otherwise force C{z=INT0}
|
|
610
|
+
(C{bool}).
|
|
611
|
+
|
|
612
|
+
@note: This point, B{C{pointB}} and B{C{pointC}} are ordered clockwise.
|
|
613
|
+
|
|
614
|
+
@return: L{Tienstra7Tuple}C{(pointP, A, B, C, a, b, c)} with survey C{pointP},
|
|
615
|
+
an instance of this (sub-)class and triangle angle C{A} at this point,
|
|
616
|
+
C{B} at B{C{pointB}} and C{C} at B{C{pointC}} in C{degrees} and
|
|
617
|
+
triangle sides C{a}, C{b} and C{c}.
|
|
618
|
+
|
|
619
|
+
@raise ResectionError: Near-coincident, -colinear or -concyclic points or sum of
|
|
620
|
+
B{C{alpha}}, B{C{beta}} and B{C{gamma}} not C{360} or
|
|
621
|
+
negative B{C{alpha}}, B{C{beta}} or B{C{gamma}}.
|
|
622
|
+
|
|
623
|
+
@raise TypeError: Invalid B{C{pointB}} or B{C{pointC}}.
|
|
624
|
+
|
|
625
|
+
@see: Function L{pygeodesy.tienstra7} for references and more details.
|
|
626
|
+
'''
|
|
627
|
+
return self._resections.tienstra7(self, pointB, pointC, alpha, beta, gamma,
|
|
628
|
+
useZ=useZ, datum=self.datum)
|
|
629
|
+
|
|
630
|
+
@deprecated_method
|
|
631
|
+
def to2ab(self): # PYCHOK no cover
|
|
632
|
+
'''DEPRECATED, use property C{philam}.
|
|
633
|
+
|
|
634
|
+
@return: A L{PhiLam2Tuple}C{(phi, lam)}.
|
|
635
|
+
'''
|
|
636
|
+
return self.philam
|
|
637
|
+
|
|
638
|
+
@deprecated_method
|
|
639
|
+
def to2ll(self): # PYCHOK no cover
|
|
640
|
+
'''DEPRECATED, use property C{latlon}.
|
|
641
|
+
|
|
642
|
+
@return: A L{LatLon2Tuple}C{(lat, lon)}.
|
|
643
|
+
'''
|
|
644
|
+
return self.latlon
|
|
645
|
+
|
|
646
|
+
@deprecated_method
|
|
647
|
+
def to3llh(self, datum=None): # PYCHOK no cover
|
|
648
|
+
'''DEPRECATED, use property L{latlonheight} or L{latlonheightdatum}.
|
|
649
|
+
|
|
650
|
+
@return: A L{LatLon4Tuple}C{(lat, lon, height, datum)}.
|
|
651
|
+
|
|
652
|
+
@note: This method returns a B{C{-4Tuple}} I{and not a} C{-3Tuple}
|
|
653
|
+
as its name may suggest.
|
|
654
|
+
'''
|
|
655
|
+
t = self.toLatLon(datum=datum, LatLon=None)
|
|
656
|
+
return LatLon4Tuple(t.lat, t.lon, t.height, t.datum, name=self.name)
|
|
657
|
+
|
|
658
|
+
# def _to3LLh(self, datum, LL, **pairs): # OBSOLETE
|
|
659
|
+
# '''(INTERNAL) Helper for C{subclass.toLatLon} and C{.to3llh}.
|
|
660
|
+
# '''
|
|
661
|
+
# r = self.to3llh(datum) # LatLon3Tuple
|
|
662
|
+
# if LL is not None:
|
|
663
|
+
# r = LL(r.lat, r.lon, height=r.height, datum=datum, name=self.name)
|
|
664
|
+
# for n, v in pairs.items():
|
|
665
|
+
# setattr(r, n, v)
|
|
666
|
+
# return r
|
|
667
|
+
|
|
668
|
+
def toDatum(self, datum2, datum=None):
|
|
669
|
+
'''Convert this cartesian from one datum to an other.
|
|
670
|
+
|
|
671
|
+
@arg datum2: Datum to convert I{to} (L{Datum}).
|
|
672
|
+
@kwarg datum: Datum to convert I{from} (L{Datum}).
|
|
673
|
+
|
|
674
|
+
@return: The converted point (C{Cartesian}).
|
|
675
|
+
|
|
676
|
+
@raise TypeError: B{C{datum2}} or B{C{datum}}
|
|
677
|
+
invalid.
|
|
678
|
+
'''
|
|
679
|
+
_xinstanceof(Datum, datum2=datum2)
|
|
680
|
+
|
|
681
|
+
c = self if datum in (None, self.datum) else \
|
|
682
|
+
self.toDatum(datum)
|
|
683
|
+
|
|
684
|
+
i, d = False, c.datum
|
|
685
|
+
if d == datum2:
|
|
686
|
+
return c.copy() if c is self else c
|
|
687
|
+
|
|
688
|
+
elif datum2.transform.isunity and \
|
|
689
|
+
d.transform.isunity:
|
|
690
|
+
return c.dup(datum=datum2)
|
|
691
|
+
|
|
692
|
+
elif d == _WGS84:
|
|
693
|
+
d = datum2 # convert from WGS84 to datum2
|
|
694
|
+
|
|
695
|
+
elif datum2 == _WGS84:
|
|
696
|
+
i = True # convert to WGS84 by inverse transformation
|
|
697
|
+
|
|
698
|
+
else: # neither datum2 nor c.datum is WGS84, invert to WGS84 first
|
|
699
|
+
c = c.toTransform(d.transform, inverse=True, datum=_WGS84)
|
|
700
|
+
d = datum2
|
|
701
|
+
|
|
702
|
+
return c.toTransform(d.transform, inverse=i, datum=datum2)
|
|
703
|
+
|
|
704
|
+
def toEcef(self):
|
|
705
|
+
'''Convert this cartesian to I{geodetic} (lat-/longitude) coordinates.
|
|
706
|
+
|
|
707
|
+
@return: An L{Ecef9Tuple}C{(x, y, z, lat, lon, height,
|
|
708
|
+
C, M, datum)} with C{C} and C{M} if available.
|
|
709
|
+
|
|
710
|
+
@raise EcefError: A C{.datum} or an ECEF issue.
|
|
711
|
+
'''
|
|
712
|
+
return self._ecef9
|
|
713
|
+
|
|
714
|
+
def toLatLon(self, datum=None, height=None, LatLon=None, **LatLon_kwds): # see .ecef.Ecef9Tuple.toDatum
|
|
715
|
+
'''Convert this cartesian to a I{geodetic} (lat-/longitude) point.
|
|
716
|
+
|
|
717
|
+
@kwarg datum: Optional datum (L{Datum}, L{Ellipsoid}, L{Ellipsoid2}
|
|
718
|
+
or L{a_f2Tuple}).
|
|
719
|
+
@kwarg height: Optional height, overriding the converted height
|
|
720
|
+
(C{meter}), iff B{C{LatLon}} is not C{None}.
|
|
721
|
+
@kwarg LatLon: Optional class to return the geodetic point
|
|
722
|
+
(C{LatLon}) or C{None}.
|
|
723
|
+
@kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword
|
|
724
|
+
arguments, ignored if C{B{LatLon} is None}.
|
|
725
|
+
|
|
726
|
+
@return: The geodetic point (B{C{LatLon}}) or if B{C{LatLon}}
|
|
727
|
+
is C{None}, an L{Ecef9Tuple}C{(x, y, z, lat, lon,
|
|
728
|
+
height, C, M, datum)} with C{C} and C{M} if available.
|
|
729
|
+
|
|
730
|
+
@raise TypeError: Invalid B{C{datum}} or B{C{LatLon_kwds}}.
|
|
731
|
+
'''
|
|
732
|
+
d = _spherical_datum(datum or self.datum, name=self.name)
|
|
733
|
+
if d == self.datum:
|
|
734
|
+
r = self.toEcef()
|
|
735
|
+
else:
|
|
736
|
+
c = self.toDatum(d)
|
|
737
|
+
r = c.Ecef(d, name=self.name).reverse(c, M=LatLon is None)
|
|
738
|
+
|
|
739
|
+
if LatLon: # class or .classof
|
|
740
|
+
h = _heigHt(r, height)
|
|
741
|
+
r = LatLon(r.lat, r.lon, datum=r.datum, height=h,
|
|
742
|
+
**_xkwds(LatLon_kwds, name=r.name))
|
|
743
|
+
_xdatum(r.datum, d)
|
|
744
|
+
return r
|
|
745
|
+
|
|
746
|
+
def toLocal(self, Xyz=None, ltp=None, **Xyz_kwds):
|
|
747
|
+
'''Convert this I{geocentric} cartesian to I{local} C{X}, C{Y} and C{Z}.
|
|
748
|
+
|
|
749
|
+
@kwarg Xyz: Optional class to return C{X}, C{Y} and C{Z}
|
|
750
|
+
(L{XyzLocal}, L{Enu}, L{Ned}) or C{None}.
|
|
751
|
+
@kwarg ltp: The I{local tangent plane} (LTP) to use,
|
|
752
|
+
overriding this cartesian's LTP (L{Ltp}).
|
|
753
|
+
@kwarg Xyz_kwds: Optional, additional B{C{Xyz}} keyword
|
|
754
|
+
arguments, ignored if C{B{Xyz} is None}.
|
|
755
|
+
|
|
756
|
+
@return: An B{C{Xyz}} instance or if C{B{Xyz} is None},
|
|
757
|
+
a L{Local9Tuple}C{(x, y, z, lat, lon, height,
|
|
758
|
+
ltp, ecef, M)} with C{M=None} always.
|
|
759
|
+
|
|
760
|
+
@raise TypeError: Invalid B{C{ltp}}.
|
|
761
|
+
'''
|
|
762
|
+
p = self._ltp._xLtp(ltp, self._Ltp)
|
|
763
|
+
return p._ecef2local(self._ecef9, Xyz, Xyz_kwds)
|
|
764
|
+
|
|
765
|
+
def toLtp(self, Ecef=None):
|
|
766
|
+
'''Return the I{local tangent plane} (LTP) for this cartesian.
|
|
767
|
+
|
|
768
|
+
@kwarg Ecef: Optional ECEF I{class} (L{EcefKarney}, ...
|
|
769
|
+
L{EcefYou}), overriding this cartesian's C{Ecef}.
|
|
770
|
+
'''
|
|
771
|
+
return self._Ltp if Ecef in (None, self.Ecef) else self._ltp.Ltp(
|
|
772
|
+
self._ecef9, ecef=Ecef(self.datum), name=self.name)
|
|
773
|
+
|
|
774
|
+
def toNvector(self, Nvector=None, datum=None, **Nvector_kwds):
|
|
775
|
+
'''Convert this cartesian to C{n-vector} components, I{including height}.
|
|
776
|
+
|
|
777
|
+
@kwarg Nvector: Optional class to return the C{n-vector} components
|
|
778
|
+
(C{Nvector}) or C{None}.
|
|
779
|
+
@kwarg datum: Optional datum (L{Datum}, L{Ellipsoid}, L{Ellipsoid2}
|
|
780
|
+
or L{a_f2Tuple}) overriding this cartesian's datum.
|
|
781
|
+
@kwarg Nvector_kwds: Optional, additional B{C{Nvector}} keyword
|
|
782
|
+
arguments, ignored if C{B{Nvector} is None}.
|
|
783
|
+
|
|
784
|
+
@return: An B{C{Nvector}} or a L{Vector4Tuple}C{(x, y, z, h)} if
|
|
785
|
+
B{C{Nvector}} is C{None}.
|
|
786
|
+
|
|
787
|
+
@raise TypeError: Invalid B{C{Nvector}}, B{C{Nvector_kwds}} or
|
|
788
|
+
B{C{datum}}.
|
|
789
|
+
|
|
790
|
+
@raise ValueError: B{C{Cartesian}} at origin.
|
|
791
|
+
'''
|
|
792
|
+
r, d = self._N_vector.xyzh, self.datum
|
|
793
|
+
if datum is not None:
|
|
794
|
+
d = _spherical_datum(datum, name=self.name)
|
|
795
|
+
if d != self.datum:
|
|
796
|
+
r = self._n_xyzh4(d)
|
|
797
|
+
|
|
798
|
+
if Nvector is not None:
|
|
799
|
+
kwds = _xkwds(Nvector_kwds, h=r.h, datum=d)
|
|
800
|
+
r = self._xnamed(Nvector(r.x, r.y, r.z, **kwds))
|
|
801
|
+
return r
|
|
802
|
+
|
|
803
|
+
def toRtp(self):
|
|
804
|
+
'''Convert this cartesian to I{spherical, polar} coordinates.
|
|
805
|
+
|
|
806
|
+
@return: L{RadiusThetaPhi3Tuple}C{(r, theta, phi)} with C{theta}
|
|
807
|
+
and C{phi}, both in L{Degrees}.
|
|
808
|
+
|
|
809
|
+
@see: Function L{xyz2rtp_} and class L{RadiusThetaPhi3Tuple}.
|
|
810
|
+
'''
|
|
811
|
+
return _rtp3(self.toRtp, Degrees, self, name=self.name)
|
|
812
|
+
|
|
813
|
+
def toStr(self, prec=3, fmt=Fmt.SQUARE, sep=_COMMASPACE_): # PYCHOK expected
|
|
814
|
+
'''Return the string representation of this cartesian.
|
|
815
|
+
|
|
816
|
+
@kwarg prec: Number of (decimal) digits, unstripped (C{int}).
|
|
817
|
+
@kwarg fmt: Enclosing backets format (C{letter}).
|
|
818
|
+
@kwarg sep: Separator to join (C{str}).
|
|
819
|
+
|
|
820
|
+
@return: Cartesian represented as "[x, y, z]" (C{str}).
|
|
821
|
+
'''
|
|
822
|
+
return Vector3d.toStr(self, prec=prec, fmt=fmt, sep=sep)
|
|
823
|
+
|
|
824
|
+
def toTransform(self, transform, inverse=False, datum=None):
|
|
825
|
+
'''Apply a Helmert transform to this cartesian.
|
|
826
|
+
|
|
827
|
+
@arg transform: Transform to apply (L{Transform} or L{TransformXform}).
|
|
828
|
+
@kwarg inverse: Apply the inverse of the C{B{transform}} (C{bool}).
|
|
829
|
+
@kwarg datum: Datum for the transformed cartesian (L{Datum}), overriding
|
|
830
|
+
this cartesian's datum but I{not} taken it into account.
|
|
831
|
+
|
|
832
|
+
@return: A transformed cartesian (C{Cartesian}) or a copy of this
|
|
833
|
+
cartesian if C{B{transform}.isunity}.
|
|
834
|
+
|
|
835
|
+
@raise TypeError: Invalid B{C{transform}}.
|
|
836
|
+
'''
|
|
837
|
+
_xinstanceof(Transform, transform=transform)
|
|
838
|
+
if transform.isunity:
|
|
839
|
+
c = self.dup(datum=datum or self.datum)
|
|
840
|
+
else:
|
|
841
|
+
# if inverse and d != _WGS84:
|
|
842
|
+
# raise _ValueError(inverse=inverse, datum=d,
|
|
843
|
+
# txt=_not_(_WGS84.name))
|
|
844
|
+
xyz = transform.transform(*self.xyz, inverse=inverse)
|
|
845
|
+
c = self.dup(xyz=xyz, datum=datum or self.datum)
|
|
846
|
+
return c
|
|
847
|
+
|
|
848
|
+
def toVector(self, Vector=None, **Vector_kwds):
|
|
849
|
+
'''Return this cartesian's I{geocentric} components as vector.
|
|
850
|
+
|
|
851
|
+
@kwarg Vector: Optional class to return the I{geocentric}
|
|
852
|
+
components (L{Vector3d}) or C{None}.
|
|
853
|
+
@kwarg Vector_kwds: Optional, additional B{C{Vector}} keyword
|
|
854
|
+
arguments, ignored if C{B{Vector} is None}.
|
|
855
|
+
|
|
856
|
+
@return: A B{C{Vector}} or a L{Vector3Tuple}C{(x, y, z)} if
|
|
857
|
+
B{C{Vector}} is C{None}.
|
|
858
|
+
|
|
859
|
+
@raise TypeError: Invalid B{C{Vector}} or B{C{Vector_kwds}}.
|
|
860
|
+
'''
|
|
861
|
+
return self.xyz if Vector is None else self._xnamed(
|
|
862
|
+
Vector(self.x, self.y, self.z, **Vector_kwds))
|
|
863
|
+
|
|
864
|
+
|
|
865
|
+
class RadiusThetaPhi3Tuple(_NamedTuple):
|
|
866
|
+
'''3-Tuple C{(r, theta, phi)} with radial distance C{r} in C{meter}, inclination
|
|
867
|
+
C{theta} (with respect to the positive z-axis) and azimuthal angle C{phi} in
|
|
868
|
+
L{Degrees} I{or} L{Radians} representing a U{spherical, polar position
|
|
869
|
+
<https://WikiPedia.org/wiki/Spherical_coordinate_system>}.
|
|
870
|
+
'''
|
|
871
|
+
_Names_ = (_r_, _theta_, _phi_)
|
|
872
|
+
_Units_ = ( Meter, _Pass, _Pass)
|
|
873
|
+
|
|
874
|
+
def toCartesian(self, name=NN, **Cartesian_and_kwds):
|
|
875
|
+
'''Convert this L{RadiusThetaPhi3Tuple} to a cartesian C{(x, y, z)} vector.
|
|
876
|
+
|
|
877
|
+
@kwarg name: Optional name (C{str}), overriding this name.
|
|
878
|
+
@kwarg Cartesian_and_kwds: Optional C{B{Cartesian}=None} class and additional
|
|
879
|
+
C{B{Cartesian}} keyword arguments.
|
|
880
|
+
|
|
881
|
+
@return: A C{B{Cartesian}(x, y, z)} instance or if no C{B{Cartesian}} keyword
|
|
882
|
+
argument is given, a L{Vector3Tuple}C{(x, y, z)} with C{x}, C{y}
|
|
883
|
+
and C{z} in the same units as radius C{r}, C{meter} conventionally.
|
|
884
|
+
|
|
885
|
+
@see: Function L{rtp2xyz_}.
|
|
886
|
+
'''
|
|
887
|
+
r, t, p = self
|
|
888
|
+
t, p, _ = _toRadians(self, t, p)
|
|
889
|
+
return rtp2xyz_(r, t, p, name=name or self.name, **Cartesian_and_kwds)
|
|
890
|
+
|
|
891
|
+
def toDegrees(self, name=NN):
|
|
892
|
+
'''Convert this L{RadiusThetaPhi3Tuple}'s angles to L{Degrees}.
|
|
893
|
+
|
|
894
|
+
@kwarg name: Optional name (C{str}), overriding this name.
|
|
895
|
+
|
|
896
|
+
@return: L{RadiusThetaPhi3Tuple}C{(r, theta, phi)} with C{theta}
|
|
897
|
+
and C{phi} both in L{Degrees}.
|
|
898
|
+
'''
|
|
899
|
+
r, t, p = self
|
|
900
|
+
t, p, _ = _toDegrees(self, t, p)
|
|
901
|
+
return _ or self.classof(r, Degrees(theta=t), Degrees(phi=p),
|
|
902
|
+
name=name or self.name)
|
|
903
|
+
|
|
904
|
+
def toRadians(self, name=NN):
|
|
905
|
+
'''Convert this L{RadiusThetaPhi3Tuple}'s angles to L{Radians}.
|
|
906
|
+
|
|
907
|
+
@kwarg name: Optional name (C{str}), overriding this name.
|
|
908
|
+
|
|
909
|
+
@return: L{RadiusThetaPhi3Tuple}C{(r, theta, phi)} with C{theta}
|
|
910
|
+
and C{phi} both in L{Radians}.
|
|
911
|
+
'''
|
|
912
|
+
r, t, p = self
|
|
913
|
+
t, p, _ = _toRadians(self, t, p)
|
|
914
|
+
return _ or self.classof(r, Radians(theta=t), Radians(phi=p),
|
|
915
|
+
name=name or self.name)
|
|
916
|
+
|
|
917
|
+
|
|
918
|
+
def rtp2xyz(r_rtp, *theta_phi, **name_Cartesian_and_kwds):
|
|
919
|
+
'''Convert I{spherical, polar} C{(r, theta, phi)} to cartesian C{(x, y, z)} coordinates.
|
|
920
|
+
|
|
921
|
+
@arg r_rtp: Radial distance (C{scalar}, conventially C{meter}) or a previous
|
|
922
|
+
L{RadiusThetaPhi3Tuple} instance.
|
|
923
|
+
@arg theta_phi: Inclination B{C{theta}} (C{degrees} with respect to the positive z-axis)
|
|
924
|
+
and azimuthal angle B{C{phi}} (C{degrees}), ignored if C{B{r_rtp}} is a
|
|
925
|
+
L{RadiusThetaPhi3Tuple}.
|
|
926
|
+
@kwarg name_Cartesian_and_kwds: Optional C{B{name}=NN} (C{str}), a C{B{Cartesian}=None}
|
|
927
|
+
class to return the coordinates and additional C{B{Cartesian}}
|
|
928
|
+
keyword arguments.
|
|
929
|
+
|
|
930
|
+
@return: A C{B{Cartesian}(x, y, z)} instance or if no C{B{Cartesian}} keyword argument
|
|
931
|
+
is given a L{Vector3Tuple}C{(x, y, z)}, with C{x}, C{y} and C{z} in the same
|
|
932
|
+
units as radius C{r}, C{meter} conventionally.
|
|
933
|
+
|
|
934
|
+
@raise TypeError: Invalid B{C{r_rtp}}.
|
|
935
|
+
|
|
936
|
+
@see: Functions L{rtp2xyz_} and L{xyz2rtp}.
|
|
937
|
+
'''
|
|
938
|
+
if isinstance(r_rtp, RadiusThetaPhi3Tuple):
|
|
939
|
+
c = r_rtp.toCartesian(**name_Cartesian_and_kwds)
|
|
940
|
+
else:
|
|
941
|
+
c = rtp2xyz_(r_rtp, *map(radians, theta_phi), **name_Cartesian_and_kwds)
|
|
942
|
+
return c
|
|
943
|
+
|
|
944
|
+
|
|
945
|
+
def rtp2xyz_(r_rtp, *theta_phi, **name_Cartesian_and_kwds):
|
|
946
|
+
'''Convert I{spherical, polar} C{(r, theta, phi)} to cartesian C{(x, y, z)} coordinates.
|
|
947
|
+
|
|
948
|
+
@arg r_rtp: Radial distance (C{scalar}, conventially C{meter}) or a previous
|
|
949
|
+
L{RadiusThetaPhi3Tuple} instance.
|
|
950
|
+
@arg theta_phi: Inclination B{C{theta}} (C{radians} with respect to the positive z-axis)
|
|
951
|
+
and azimuthal angle B{C{phi}} (C{degrees}), ignored is C{B{r_rtp}} is a
|
|
952
|
+
L{RadiusThetaPhi3Tuple}.
|
|
953
|
+
@kwarg name_Cartesian_and_kwds: Optional C{B{name}=NN} (C{str}), a C{B{Cartesian}=None}
|
|
954
|
+
class to return the coordinates and additional C{B{Cartesian}}
|
|
955
|
+
keyword arguments.
|
|
956
|
+
|
|
957
|
+
@return: A C{B{Cartesian}(x, y, z)} instance or if no C{B{Cartesian}} keyword argument
|
|
958
|
+
is given a L{Vector3Tuple}C{(x, y, z)}, with C{x}, C{y} and C{z} in the same
|
|
959
|
+
units as radius C{r}, C{meter} conventionally.
|
|
960
|
+
|
|
961
|
+
@raise TypeError: Invalid B{C{r_rtp}} or B{C{theta_phi}}.
|
|
962
|
+
|
|
963
|
+
@see: U{Physics convention<https://WikiPedia.org/wiki/Spherical_coordinate_system>}
|
|
964
|
+
(ISO 80000-2:2019).
|
|
965
|
+
'''
|
|
966
|
+
if _isMeter(r_rtp) and len(theta_phi) == 2:
|
|
967
|
+
r = r_rtp
|
|
968
|
+
if r and _isfinite(r):
|
|
969
|
+
s, z, y, x = sincos2_(*theta_phi)
|
|
970
|
+
s *= r
|
|
971
|
+
z *= r
|
|
972
|
+
y *= s
|
|
973
|
+
x *= s
|
|
974
|
+
else:
|
|
975
|
+
x = y = z = r
|
|
976
|
+
|
|
977
|
+
def _n_C_kwds3(name=NN, Cartesian=None, **kwds):
|
|
978
|
+
return name, Cartesian, kwds
|
|
979
|
+
|
|
980
|
+
n, C, kwds = _n_C_kwds3(**name_Cartesian_and_kwds)
|
|
981
|
+
c = Vector3Tuple(x, y, z, name=n) if C is None else \
|
|
982
|
+
C(x, y, z, name=n, **kwds)
|
|
983
|
+
|
|
984
|
+
elif isinstance(r_rtp, RadiusThetaPhi3Tuple):
|
|
985
|
+
c = r_rtp.toCartesian(**name_Cartesian_and_kwds)
|
|
986
|
+
else:
|
|
987
|
+
raise _TypeError(r_rtp=r_rtp, theta_phi=theta_phi)
|
|
988
|
+
return c
|
|
989
|
+
|
|
990
|
+
|
|
991
|
+
def _rtp3(where, Unit, *x_y_z, **name):
|
|
992
|
+
'''(INTERNAL) Helper for C{.toRtp}, C{xyz2rtp} and C{xyz2rtp_}.
|
|
993
|
+
'''
|
|
994
|
+
x, y, z = _MODS.vector3dBase._xyz3(where, *x_y_z)
|
|
995
|
+
r = hypot_(x, y, z)
|
|
996
|
+
if r > 0:
|
|
997
|
+
t = acos1(z / r)
|
|
998
|
+
p = atan2(y, x)
|
|
999
|
+
while p < 0:
|
|
1000
|
+
p += PI2
|
|
1001
|
+
if Unit is Degrees:
|
|
1002
|
+
t = degrees(t)
|
|
1003
|
+
p = degrees(p)
|
|
1004
|
+
else:
|
|
1005
|
+
t = p = _0_0
|
|
1006
|
+
return RadiusThetaPhi3Tuple(r, Unit(theta=t), Unit(phi=p), **name)
|
|
1007
|
+
|
|
1008
|
+
|
|
1009
|
+
def xyz2rtp(x_xyz, *y_z, **name):
|
|
1010
|
+
'''Convert cartesian C{(x, y, z)} to I{spherical, polar} C{(r, theta, phi)} coordinates.
|
|
1011
|
+
|
|
1012
|
+
@return: L{RadiusThetaPhi3Tuple}C{(r, theta, phi)} with C{theta} and C{phi},
|
|
1013
|
+
both in L{Degrees}.
|
|
1014
|
+
|
|
1015
|
+
@see: Function L{xyz2rtp_} and class L{RadiusThetaPhi3Tuple}.
|
|
1016
|
+
'''
|
|
1017
|
+
return _rtp3(xyz2rtp, Degrees, x_xyz, *y_z, **name)
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
def xyz2rtp_(x_xyz, *y_z, **name):
|
|
1021
|
+
'''Convert cartesian C{(x, y, z)} to I{spherical, polar} C{(r, theta, phi)} coordinates.
|
|
1022
|
+
|
|
1023
|
+
@arg x_xyz: X component (C{scalar}) or a cartesian (C{Cartesian}, L{Ecef9Tuple},
|
|
1024
|
+
C{Nvector}, L{Vector3d}, L{Vector3Tuple}, L{Vector4Tuple} or a
|
|
1025
|
+
C{tuple} or C{list} of 3+ C{scalar} items) if no C{y_z} specified.
|
|
1026
|
+
@arg y_z: Y and Z component (C{scalar}s), ignored if C{x_xyz} is not a C{scalar}.
|
|
1027
|
+
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
1028
|
+
|
|
1029
|
+
@return: L{RadiusThetaPhi3Tuple}C{(r, theta, phi)} with radial distance C{r}
|
|
1030
|
+
(C{meter}, same units as C{x}, C{y} and C{z}), inclination C{theta}
|
|
1031
|
+
(with respect to the positive z-axis) and azimuthal angle C{phi},
|
|
1032
|
+
both in L{Radians}.
|
|
1033
|
+
|
|
1034
|
+
@see: U{Physics convention<https://WikiPedia.org/wiki/Spherical_coordinate_system>}
|
|
1035
|
+
(ISO 80000-2:2019).
|
|
1036
|
+
'''
|
|
1037
|
+
return _rtp3(xyz2rtp_, Radians, x_xyz, *y_z, **name)
|
|
1038
|
+
|
|
1039
|
+
|
|
1040
|
+
__all__ += _ALL_DOCS(CartesianBase)
|
|
1041
|
+
|
|
1042
|
+
# **) MIT License
|
|
1043
|
+
#
|
|
1044
|
+
# Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
|
|
1045
|
+
#
|
|
1046
|
+
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
1047
|
+
# copy of this software and associated documentation files (the "Software"),
|
|
1048
|
+
# to deal in the Software without restriction, including without limitation
|
|
1049
|
+
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
1050
|
+
# and/or sell copies of the Software, and to permit persons to whom the
|
|
1051
|
+
# Software is furnished to do so, subject to the following conditions:
|
|
1052
|
+
#
|
|
1053
|
+
# The above copyright notice and this permission notice shall be included
|
|
1054
|
+
# in all copies or substantial portions of the Software.
|
|
1055
|
+
#
|
|
1056
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
1057
|
+
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
1058
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
1059
|
+
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
1060
|
+
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
1061
|
+
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
1062
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|