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,685 @@
|
|
|
1
|
+
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
u'''Ellipsoidal, C{N-vector}-based geodesy.
|
|
5
|
+
|
|
6
|
+
Ellipsoidal classes geodetic L{LatLon}, geocentric (ECEF) L{Cartesian}
|
|
7
|
+
and C{Nvector} and DEPRECATED L{Ned} and functions L{meanOf}, L{sumOf}
|
|
8
|
+
and DEPRECATED L{toNed}.
|
|
9
|
+
|
|
10
|
+
Pure Python implementation of n-vector-based geodetic (lat-/longitude)
|
|
11
|
+
methods by I{(C) Chris Veness 2011-2016} published under the same MIT
|
|
12
|
+
Licence**, see U{Vector-based geodesy
|
|
13
|
+
<https://www.Movable-Type.co.UK/scripts/latlong-vectors.html>}.
|
|
14
|
+
|
|
15
|
+
These classes and functions work with: (a) geodetic lat-/longitude points on
|
|
16
|
+
the earth's surface and (b) 3-D vectors used as n-vectors representing points
|
|
17
|
+
on the earth's surface or vectors normal to the plane of a great circle.
|
|
18
|
+
|
|
19
|
+
See also I{Kenneth Gade} U{'A Non-singular Horizontal Position Representation'
|
|
20
|
+
<https://www.NavLab.net/Publications/A_Nonsingular_Horizontal_Position_Representation.pdf>},
|
|
21
|
+
The Journal of Navigation (2010), vol 63, nr 3, pp 395-417.
|
|
22
|
+
'''
|
|
23
|
+
# make sure int/int division yields float quotient, see .basics
|
|
24
|
+
from __future__ import division as _; del _ # PYCHOK semicolon
|
|
25
|
+
|
|
26
|
+
from pygeodesy.basics import issubclassof, map2, _xinstanceof
|
|
27
|
+
from pygeodesy.datums import _earth_ellipsoid, _ellipsoidal_datum, _WGS84
|
|
28
|
+
# from pygeodesy.dms import toDMS # _MODS
|
|
29
|
+
from pygeodesy.ellipsoidalBase import CartesianEllipsoidalBase, \
|
|
30
|
+
_nearestOn, LatLonEllipsoidalBase, \
|
|
31
|
+
_TOL_M, _Wrap
|
|
32
|
+
from pygeodesy.errors import _IsnotError, _xkwds, _xkwds_pop2
|
|
33
|
+
# from pygeodesy.fmath import fdot # from .nvectorBase
|
|
34
|
+
from pygeodesy.interns import NN, _Nv00_, _COMMASPACE_
|
|
35
|
+
from pygeodesy.interns import _down_, _east_, _north_, _pole_ # PYCHOK used!
|
|
36
|
+
from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS, _ALL_OTHER
|
|
37
|
+
# from pygeodesy.ltp import Ltp # _MODS
|
|
38
|
+
from pygeodesy.ltpTuples import Aer as _Aer, Ned as _Ned, Ned4Tuple, \
|
|
39
|
+
sincos2d_, _xnamed
|
|
40
|
+
# from pygeodesy.named import _xnamed # from .ltpTuples
|
|
41
|
+
from pygeodesy.nvectorBase import fabs, fdot, NorthPole, LatLonNvectorBase, \
|
|
42
|
+
NvectorBase, sumOf as _sumOf
|
|
43
|
+
from pygeodesy.props import deprecated_class, deprecated_function, \
|
|
44
|
+
deprecated_method, Property_RO, property_RO
|
|
45
|
+
from pygeodesy.streprs import Fmt, fstr, _xzipairs
|
|
46
|
+
from pygeodesy.units import Bearing, Distance, Height, Scalar
|
|
47
|
+
# from pygeodesy.utily import sincos2d_, _Wrap # from .ltpTuples, .ellipsoidalBase
|
|
48
|
+
|
|
49
|
+
# from math import fabs # from .nvectorBase
|
|
50
|
+
|
|
51
|
+
__all__ = _ALL_LAZY.ellipsoidalNvector
|
|
52
|
+
__version__ = '24.02.18'
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class Ned(_Ned):
|
|
56
|
+
'''DEPRECATED on 2024.02.04, use class L{pygeodesy.Ned}.'''
|
|
57
|
+
|
|
58
|
+
def __init__(self, north, east, down, name=NN):
|
|
59
|
+
deprecated_class(self.__class__)
|
|
60
|
+
_Ned.__init__(self, north, east, down, name=name)
|
|
61
|
+
|
|
62
|
+
@deprecated_method # PYCHOK expected
|
|
63
|
+
def toRepr(self, prec=None, fmt=Fmt.SQUARE, sep=_COMMASPACE_, **unused):
|
|
64
|
+
'''DEPRECATED, use class L{pygeodesy.Ned}.
|
|
65
|
+
|
|
66
|
+
@kwarg prec: Number of (decimal) digits, unstripped (C{int}).
|
|
67
|
+
@kwarg fmt: Enclosing backets format (C{str}).
|
|
68
|
+
@kwarg sep: Separator between NEDs (C{str}).
|
|
69
|
+
|
|
70
|
+
@return: This Ned as "[L:f, B:degrees360, E:degrees90]" (C{str})
|
|
71
|
+
with length or slantrange C{L}, bearing or azimuth C{B}
|
|
72
|
+
and elevation C{E}.
|
|
73
|
+
'''
|
|
74
|
+
dms = _MODS.dms
|
|
75
|
+
t = (fstr(self.slantrange, prec=prec),
|
|
76
|
+
dms.toDMS(self.azimuth, form=dms.F_D, prec=prec, ddd=0),
|
|
77
|
+
dms.toDMS(self.elevation, form=dms.F_D, prec=prec, ddd=0))
|
|
78
|
+
return _xzipairs('LBE', t, sep=sep, fmt=fmt)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class Cartesian(CartesianEllipsoidalBase):
|
|
82
|
+
'''Extended to convert geocentric, L{Cartesian} points to
|
|
83
|
+
C{Nvector} and n-vector-based, geodetic L{LatLon}.
|
|
84
|
+
'''
|
|
85
|
+
@property_RO
|
|
86
|
+
def Ecef(self):
|
|
87
|
+
'''Get the ECEF I{class} (L{EcefVeness}), I{once}.
|
|
88
|
+
'''
|
|
89
|
+
return _Ecef()
|
|
90
|
+
|
|
91
|
+
def toLatLon(self, **LatLon_and_kwds): # PYCHOK LatLon=LatLon, datum=None
|
|
92
|
+
'''Convert this cartesian to an C{Nvector}-based geodetic point.
|
|
93
|
+
|
|
94
|
+
@kwarg LatLon_and_kwds: Optional L{LatLon}, B{C{datum}} and other
|
|
95
|
+
keyword arguments. Use C{B{LatLon}=...} to
|
|
96
|
+
override this L{LatLon} class or specify
|
|
97
|
+
C{B{LatLon} is None}.
|
|
98
|
+
|
|
99
|
+
@return: The geodetic point (L{LatLon}) or if B{C{LatLon}} is set
|
|
100
|
+
to C{None}, an L{Ecef9Tuple}C{(x, y, z, lat, lon, height,
|
|
101
|
+
C, M, datum)} with C{C} and C{M} if available.
|
|
102
|
+
|
|
103
|
+
@raise TypeError: Invalid B{C{LatLon_and_kwds}}.
|
|
104
|
+
'''
|
|
105
|
+
kwds = _xkwds(LatLon_and_kwds, LatLon=LatLon, datum=self.datum)
|
|
106
|
+
return CartesianEllipsoidalBase.toLatLon(self, **kwds)
|
|
107
|
+
|
|
108
|
+
def toNvector(self, **Nvector_and_kwds): # PYCHOK Datums.WGS84
|
|
109
|
+
'''Convert this cartesian to C{Nvector} components, I{including height}.
|
|
110
|
+
|
|
111
|
+
@kwarg Nvector_and_kwds: Optional C{Nvector}, B{C{datum}} and other
|
|
112
|
+
keyword arguments. Use C{B{Nvector}=...} to
|
|
113
|
+
override this C{Nvector} class or specify
|
|
114
|
+
C{B{Nvector} is None}.
|
|
115
|
+
|
|
116
|
+
@return: The C{n-vector} components (C{Nvector}) or if B{C{Nvector}}
|
|
117
|
+
is set to C{None}, a L{Vector4Tuple}C{(x, y, z, h)}
|
|
118
|
+
|
|
119
|
+
@raise TypeError: Invalid B{C{Nvector_and_kwds}}.
|
|
120
|
+
'''
|
|
121
|
+
kwds = _xkwds(Nvector_and_kwds, Nvector=Nvector, datum=self.datum)
|
|
122
|
+
return CartesianEllipsoidalBase.toNvector(self, **kwds)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class LatLon(LatLonNvectorBase, LatLonEllipsoidalBase):
|
|
126
|
+
'''An n-vector-based, ellipsoidal L{LatLon} point.
|
|
127
|
+
'''
|
|
128
|
+
_Nv = None # cached toNvector (C{Nvector})
|
|
129
|
+
|
|
130
|
+
def _update(self, updated, *attrs, **setters): # PYCHOK args
|
|
131
|
+
'''(INTERNAL) Zap cached attributes if updated.
|
|
132
|
+
'''
|
|
133
|
+
if updated:
|
|
134
|
+
LatLonNvectorBase._update(self, updated, _Nv=self._Nv) # special case
|
|
135
|
+
LatLonEllipsoidalBase._update(self, updated, *attrs, **setters)
|
|
136
|
+
|
|
137
|
+
# def crossTrackDistanceTo(self, start, end, radius=R_M):
|
|
138
|
+
# '''Return the (signed) distance from this point to the great
|
|
139
|
+
# circle defined by a start point and an end point or bearing.
|
|
140
|
+
#
|
|
141
|
+
# @arg start: Start point of great circle line (L{LatLon}).
|
|
142
|
+
# @arg end: End point of great circle line (L{LatLon}) or
|
|
143
|
+
# initial bearing (compass C{degrees360}) at the
|
|
144
|
+
# start point.
|
|
145
|
+
# @kwarg radius: Mean earth radius (C{meter}).
|
|
146
|
+
#
|
|
147
|
+
# @return: Distance to great circle, negative if to left or
|
|
148
|
+
# positive if to right of line (C{meter}, same units
|
|
149
|
+
# as B{C{radius}}).
|
|
150
|
+
#
|
|
151
|
+
# @raise TypeError: If B{C{start}} or B{C{end}} point is not L{LatLon}.
|
|
152
|
+
# '''
|
|
153
|
+
# self.others(start=start)
|
|
154
|
+
#
|
|
155
|
+
# if _isDegrees(end): # gc from point and bearing
|
|
156
|
+
# gc = start.greatCircle(end)
|
|
157
|
+
# else: # gc by two points
|
|
158
|
+
# gc = start.toNvector().cross(end.toNvector())
|
|
159
|
+
#
|
|
160
|
+
# # (signed) angle between point and gc normal vector
|
|
161
|
+
# v = self.toNvector()
|
|
162
|
+
# a = gc.angleTo(v, vSign=v.cross(gc))
|
|
163
|
+
# a = _copysign(PI_2, a) - a
|
|
164
|
+
# return a * float(radius)
|
|
165
|
+
|
|
166
|
+
def deltaTo(self, other, wrap=False, **Ned_and_kwds):
|
|
167
|
+
'''Calculate the local delta from this to an other point.
|
|
168
|
+
|
|
169
|
+
@note: This is a linear delta, I{unrelated} to a geodesic on the
|
|
170
|
+
ellipsoid.
|
|
171
|
+
|
|
172
|
+
@arg other: The other point (L{LatLon}).
|
|
173
|
+
@kwarg wrap: If C{True}, wrap or I{normalize} the B{C{other}}
|
|
174
|
+
point (C{bool}).
|
|
175
|
+
@kwarg Ned_and_kwds: Optional C{B{Ned}=L{Ned} class and B{name}=NN}
|
|
176
|
+
to return delta and other B{C{Ned}} keyword arguments.
|
|
177
|
+
|
|
178
|
+
@return: Delta from this to the other point (B{C{Ned}}).
|
|
179
|
+
|
|
180
|
+
@raise TypeError: The B{C{other}} point is not L{LatLon} or
|
|
181
|
+
B{C{Ned}} is not L{pygeodesy.Ned} nor
|
|
182
|
+
L{pygeodesy.Ned4Tuple} nor DEPRECATED L{Ned}.
|
|
183
|
+
|
|
184
|
+
@raise ValueError: If ellipsoids are incompatible.
|
|
185
|
+
'''
|
|
186
|
+
self.ellipsoids(other) # throws TypeError and ValueError
|
|
187
|
+
|
|
188
|
+
p = self.others(other)
|
|
189
|
+
if wrap:
|
|
190
|
+
p = _Wrap.point(p)
|
|
191
|
+
# get delta in cartesian frame
|
|
192
|
+
dc = p.toCartesian().minus(self.toCartesian())
|
|
193
|
+
# rotate dc to get delta in n-vector reference
|
|
194
|
+
# frame using the rotation matrix row vectors
|
|
195
|
+
ned_ = map2(dc.dot, self._rotation3)
|
|
196
|
+
|
|
197
|
+
N, kwds = _xkwds_pop2(Ned_and_kwds, Ned=Ned)
|
|
198
|
+
if issubclassof(N, Ned4Tuple):
|
|
199
|
+
ned_ += _MODS.ltp.Ltp(self, ecef=self.Ecef(self.datum)),
|
|
200
|
+
elif not issubclassof(N, _Ned):
|
|
201
|
+
raise _IsnotError(Fmt.sub_class(_Ned, Ned4Tuple), Ned=N)
|
|
202
|
+
return N(*ned_, **_xkwds(kwds, name=self.name))
|
|
203
|
+
|
|
204
|
+
# def destination(self, distance, bearing, radius=R_M, height=None):
|
|
205
|
+
# '''Return the destination point after traveling from this
|
|
206
|
+
# point the given distance on the given initial bearing.
|
|
207
|
+
#
|
|
208
|
+
# @arg distance: Distance traveled (C{meter}, same units as
|
|
209
|
+
# given earth B{C{radius}}).
|
|
210
|
+
# @arg bearing: Initial bearing (compass C{degrees360}).
|
|
211
|
+
# @kwarg radius: Mean earth radius (C{meter}).
|
|
212
|
+
# @kwarg height: Optional height at destination point,
|
|
213
|
+
# overriding default (C{meter}, same units
|
|
214
|
+
# as B{C{radius}}).
|
|
215
|
+
#
|
|
216
|
+
# @return: Destination point (L{LatLon}).
|
|
217
|
+
# '''
|
|
218
|
+
# r = _m2radians(distance, radius) # angular distance in radians
|
|
219
|
+
# # great circle by starting from this point on given bearing
|
|
220
|
+
# gc = self.greatCircle(bearing)
|
|
221
|
+
#
|
|
222
|
+
# v1 = self.toNvector()
|
|
223
|
+
# x = v1.times(cos(r)) # component of v2 parallel to v1
|
|
224
|
+
# y = gc.cross(v1).times(sin(r)) # component of v2 perpendicular to v1
|
|
225
|
+
#
|
|
226
|
+
# v2 = x.plus(y).unit()
|
|
227
|
+
# return v2.toLatLon(height=self.height if height is C{None} else height)
|
|
228
|
+
|
|
229
|
+
def destinationNed(self, delta):
|
|
230
|
+
'''Calculate the destination point using the supplied NED delta
|
|
231
|
+
from this point.
|
|
232
|
+
|
|
233
|
+
@arg delta: Delta from this to the other point in the local
|
|
234
|
+
tangent plane (LTP) of this point (L{Ned}).
|
|
235
|
+
|
|
236
|
+
@return: Destination point (L{LatLon}).
|
|
237
|
+
|
|
238
|
+
@raise TypeError: If B{C{delta}} is not L{pygeodesy.Ned} or
|
|
239
|
+
DEPRECATED L{Ned}.
|
|
240
|
+
'''
|
|
241
|
+
_xinstanceof(_Ned, delta=delta)
|
|
242
|
+
|
|
243
|
+
nv, ev, dv = self._rotation3
|
|
244
|
+
# convert NED delta to standard coordinate frame of n-vector
|
|
245
|
+
dn = delta.ned[:3] # XXX Ned4Tuple.to3Tuple
|
|
246
|
+
# rotate dn to get delta in cartesian (ECEF) coordinate
|
|
247
|
+
# reference frame using the rotation matrix column vectors
|
|
248
|
+
dc = Cartesian(fdot(dn, nv.x, ev.x, dv.x),
|
|
249
|
+
fdot(dn, nv.y, ev.y, dv.y),
|
|
250
|
+
fdot(dn, nv.z, ev.z, dv.z))
|
|
251
|
+
|
|
252
|
+
# apply (cartesian) delta to this Cartesian to obtain destination as cartesian
|
|
253
|
+
v = self.toCartesian().plus(dc)
|
|
254
|
+
return v.toLatLon(datum=self.datum, LatLon=self.classof) # Cartesian(v.x, v.y, v.z).toLatLon(...)
|
|
255
|
+
|
|
256
|
+
def distanceTo(self, other, radius=None, wrap=False):
|
|
257
|
+
'''I{Approximate} the distance from this to an other point.
|
|
258
|
+
|
|
259
|
+
@arg other: The other point (L{LatLon}).
|
|
260
|
+
@kwarg radius: Mean earth radius, ellipsoid or datum (C{meter},
|
|
261
|
+
L{Ellipsoid}, L{Ellipsoid2}, L{Datum} or
|
|
262
|
+
L{a_f2Tuple}), overriding the mean radius C{R1}
|
|
263
|
+
of this point's datum..
|
|
264
|
+
@kwarg wrap: If C{True}, wrap or I{normalize} and unroll the
|
|
265
|
+
B{C{other}} and angular distance (C{bool}).
|
|
266
|
+
|
|
267
|
+
@return: Distance (C{meter}, same units as B{C{radius}}).
|
|
268
|
+
|
|
269
|
+
@raise TypeError: The B{C{other}} point is not L{LatLon}.
|
|
270
|
+
|
|
271
|
+
@raise ValueError: Invalid B{C{radius}}.
|
|
272
|
+
'''
|
|
273
|
+
p = self.others(other)
|
|
274
|
+
if wrap:
|
|
275
|
+
p = _Wrap.point(p)
|
|
276
|
+
a = self._N_vector.angleTo(p._N_vector, wrap=wrap)
|
|
277
|
+
E = self.datum.ellipsoid if radius is None else _earth_ellipsoid(radius)
|
|
278
|
+
return fabs(a) * E.R1 # see .utily.radians2m
|
|
279
|
+
|
|
280
|
+
@property_RO
|
|
281
|
+
def Ecef(self):
|
|
282
|
+
'''Get the ECEF I{class} (L{EcefVeness}), I{once}.
|
|
283
|
+
'''
|
|
284
|
+
return _Ecef()
|
|
285
|
+
|
|
286
|
+
@deprecated_method
|
|
287
|
+
def equals(self, other, eps=None): # PYCHOK no cover
|
|
288
|
+
'''DEPRECATED, use method L{isequalTo}.
|
|
289
|
+
'''
|
|
290
|
+
return self.isequalTo(other, eps=eps)
|
|
291
|
+
|
|
292
|
+
def isequalTo(self, other, eps=None):
|
|
293
|
+
'''Compare this point with an other point.
|
|
294
|
+
|
|
295
|
+
@arg other: The other point (L{LatLon}).
|
|
296
|
+
@kwarg eps: Optional margin (C{float}).
|
|
297
|
+
|
|
298
|
+
@return: C{True} if points are identical, including
|
|
299
|
+
datum, I{ignoring height}, C{False} otherwise.
|
|
300
|
+
|
|
301
|
+
@raise TypeError: The B{C{other}} point is not L{LatLon}.
|
|
302
|
+
|
|
303
|
+
@raise ValueError: Invalid B{C{eps}}.
|
|
304
|
+
|
|
305
|
+
@see: Method C{isequalTo3} to include I{height}.
|
|
306
|
+
'''
|
|
307
|
+
return self.datum == self.others(other).datum and \
|
|
308
|
+
_MODS.formy._isequalTo(self, other, eps=eps)
|
|
309
|
+
|
|
310
|
+
# def greatCircle(self, bearing):
|
|
311
|
+
# '''Return the great circle heading on the given bearing
|
|
312
|
+
# from this point.
|
|
313
|
+
#
|
|
314
|
+
# Direction of vector is such that initial bearing vector
|
|
315
|
+
# b = c × p, where p is representing this point.
|
|
316
|
+
#
|
|
317
|
+
# @arg bearing: Bearing from this point (compass C{degrees360}).
|
|
318
|
+
#
|
|
319
|
+
# @return: N-vector representing great circle (C{Nvector}).
|
|
320
|
+
# '''
|
|
321
|
+
# a, b, _ = self.philamheight
|
|
322
|
+
# t = radians(bearing)
|
|
323
|
+
#
|
|
324
|
+
# sa, ca, sb, cb, st, ct = sincos2_(a, b, t)
|
|
325
|
+
# return self._xnamed(Nvector(sb * ct - sa * cb * st,
|
|
326
|
+
# -cb * ct - sa * sb * st,
|
|
327
|
+
# ca * st)
|
|
328
|
+
|
|
329
|
+
# def initialBearingTo(self, other, wrap=False):
|
|
330
|
+
# '''Return the initial bearing (forward azimuth) from
|
|
331
|
+
# this to an other point.
|
|
332
|
+
#
|
|
333
|
+
# @arg other: The other point (L{LatLon}).
|
|
334
|
+
# @kwarg wrap: If C{True}, wrap or I{normalize}
|
|
335
|
+
# and unroll the B{C{other}} (C{bool}).
|
|
336
|
+
#
|
|
337
|
+
# @return: Initial bearing (compass C{degrees360}).
|
|
338
|
+
#
|
|
339
|
+
# @raise TypeError: The B{C{other}} point is not L{LatLon}.
|
|
340
|
+
# '''
|
|
341
|
+
# p = self.others(other)
|
|
342
|
+
# if wrap:
|
|
343
|
+
# p = _Wrap.point(p)
|
|
344
|
+
# v1 = self.toNvector()
|
|
345
|
+
#
|
|
346
|
+
# gc1 = v1.cross(p.toNvector()) # gc through v1 & v2
|
|
347
|
+
# gc2 = v1.cross(_NP3) # gc through v1 & North pole
|
|
348
|
+
#
|
|
349
|
+
# # bearing is (signed) angle between gc1 & gc2
|
|
350
|
+
# return degrees360(gc1.angleTo(gc2, vSign=v1))
|
|
351
|
+
|
|
352
|
+
def intermediateTo(self, other, fraction, height=None, wrap=False):
|
|
353
|
+
'''Return the point at given fraction between this and
|
|
354
|
+
an other point.
|
|
355
|
+
|
|
356
|
+
@arg other: The other point (L{LatLon}).
|
|
357
|
+
@arg fraction: Fraction between both points (C{scalar},
|
|
358
|
+
0.0 at this to 1.0 at the other point.
|
|
359
|
+
@kwarg height: Optional height, overriding the fractional
|
|
360
|
+
height (C{meter}).
|
|
361
|
+
@kwarg wrap: If C{True}, wrap or I{normalize} the
|
|
362
|
+
B{C{other}} point (C{bool}).
|
|
363
|
+
|
|
364
|
+
@return: Intermediate point (L{LatLon}).
|
|
365
|
+
|
|
366
|
+
@raise TypeError: The B{C{other}} point is not L{LatLon}.
|
|
367
|
+
'''
|
|
368
|
+
p = self.others(other)
|
|
369
|
+
if wrap:
|
|
370
|
+
p = _Wrap.point(p)
|
|
371
|
+
f = Scalar(fraction=fraction)
|
|
372
|
+
h = self._havg(other, f=f, h=height)
|
|
373
|
+
i = self.toNvector().intermediateTo(p.toNvector(), f)
|
|
374
|
+
return i.toLatLon(height=h, LatLon=self.classof) # Nvector(i.x, i.y, i.z).toLatLon(...)
|
|
375
|
+
|
|
376
|
+
@Property_RO
|
|
377
|
+
def _rotation3(self):
|
|
378
|
+
'''(INTERNAL) Get the rotation matrix from n-vector coordinate frame axes.
|
|
379
|
+
'''
|
|
380
|
+
nv = self.toNvector() # local (n-vector) coordinate frame
|
|
381
|
+
|
|
382
|
+
dv = nv.negate() # down, opposite to n-vector
|
|
383
|
+
ev = NorthPole.cross(nv, raiser=_pole_).unit() # east, pointing perpendicular to the plane
|
|
384
|
+
nv = ev.cross(dv) # north, by right hand rule
|
|
385
|
+
return nv, ev, dv # matrix rows
|
|
386
|
+
|
|
387
|
+
def toCartesian(self, **Cartesian_and_kwds): # PYCHOK Cartesian=Cartesian, datum=None
|
|
388
|
+
'''Convert this point to an C{Nvector}-based geodetic point.
|
|
389
|
+
|
|
390
|
+
@kwarg Cartesian_and_kwds: Optional L{Cartesian}, B{C{datum}} and other
|
|
391
|
+
keyword arguments. Use C{B{Cartesian}=...}
|
|
392
|
+
to override this L{Cartesian} class or specify
|
|
393
|
+
C{B{Cartesian}=None}.
|
|
394
|
+
|
|
395
|
+
@return: The geodetic point (L{Cartesian}) or if B{C{Cartesian}} is set
|
|
396
|
+
to C{None}, an L{Ecef9Tuple}C{(x, y, z, lat, lon, height, C, M,
|
|
397
|
+
datum)} with C{C} and C{M} if available.
|
|
398
|
+
|
|
399
|
+
@raise TypeError: Invalid B{C{Cartesian}} or other B{C{Cartesian_and_kwds}}.
|
|
400
|
+
'''
|
|
401
|
+
kwds = _xkwds(Cartesian_and_kwds, Cartesian=Cartesian, datum=self.datum)
|
|
402
|
+
return LatLonEllipsoidalBase.toCartesian(self, **kwds)
|
|
403
|
+
|
|
404
|
+
def toNvector(self, **Nvector_and_kwds): # PYCHOK signature
|
|
405
|
+
'''Convert this point to C{Nvector} components, I{including height}.
|
|
406
|
+
|
|
407
|
+
@kwarg Nvector_and_kwds: Optional C{Nvector}, B{C{datum}} and other
|
|
408
|
+
keyword arguments. Use C{B{Nvector}=...}
|
|
409
|
+
to override this C{Nvector} class or specify
|
|
410
|
+
C{B{Nvector}=None}.
|
|
411
|
+
|
|
412
|
+
@return: The C{n-vector} components (C{Nvector}) or if B{C{Nvector}}
|
|
413
|
+
is set to C{None}, a L{Vector4Tuple}C{(x, y, z, h)}.
|
|
414
|
+
|
|
415
|
+
@raise TypeError: Invalid B{C{Nvector}} or other B{C{Nvector_and_kwds}}.
|
|
416
|
+
'''
|
|
417
|
+
kwds = _xkwds(Nvector_and_kwds, Nvector=Nvector, datum=self.datum)
|
|
418
|
+
return LatLonNvectorBase.toNvector(self, **kwds)
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
_Nvll = LatLon(0, 0, name=_Nv00_) # reference instance (L{LatLon})
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
class Nvector(NvectorBase):
|
|
425
|
+
'''An n-vector is a position representation using a (unit) vector
|
|
426
|
+
normal to the earth ellipsoid. Unlike lat-/longitude points,
|
|
427
|
+
n-vectors have no singularities or discontinuities.
|
|
428
|
+
|
|
429
|
+
For many applications, n-vectors are more convenient to work
|
|
430
|
+
with than other position representations like lat-/longitude,
|
|
431
|
+
earth-centred earth-fixed (ECEF) vectors, UTM coordinates, etc.
|
|
432
|
+
|
|
433
|
+
Note commonality with L{pygeodesy.sphericalNvector.Nvector}.
|
|
434
|
+
'''
|
|
435
|
+
_datum = _WGS84 # default datum (L{Datum})
|
|
436
|
+
|
|
437
|
+
def __init__(self, x_xyz, y=None, z=None, h=0, datum=None, ll=None, name=NN):
|
|
438
|
+
'''New n-vector normal to the earth's surface.
|
|
439
|
+
|
|
440
|
+
@arg x_xyz: X component of vector (C{scalar}) or (3-D) vector
|
|
441
|
+
(C{Nvector}, L{Vector3d}, L{Vector3Tuple} or
|
|
442
|
+
L{Vector4Tuple}).
|
|
443
|
+
@kwarg y: Y component of vector (C{scalar}), ignored if B{C{x_xyz}}
|
|
444
|
+
is not C{scalar}, otherwise same units as B{C{x_xyz}}.
|
|
445
|
+
@kwarg z: Z component of vector (C{scalar}), ignored if B{C{x_xyz}}
|
|
446
|
+
is not C{scalar}, otherwise same units as B{C{x_xyz}}.
|
|
447
|
+
@kwarg h: Optional height above model surface (C{meter}).
|
|
448
|
+
@kwarg datum: Optional datum this n-vector is defined in
|
|
449
|
+
(L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or
|
|
450
|
+
L{a_f2Tuple}).
|
|
451
|
+
@kwarg ll: Optional, original latlon (C{LatLon}).
|
|
452
|
+
@kwarg name: Optional name (C{str}).
|
|
453
|
+
|
|
454
|
+
@raise TypeError: If B{C{datum}} is not a L{Datum}.
|
|
455
|
+
'''
|
|
456
|
+
NvectorBase.__init__(self, x_xyz, y=y, z=z, h=h, ll=ll, name=name)
|
|
457
|
+
if datum not in (None, self._datum):
|
|
458
|
+
self._datum = _ellipsoidal_datum(datum, name=name)
|
|
459
|
+
|
|
460
|
+
@Property_RO
|
|
461
|
+
def datum(self):
|
|
462
|
+
'''Get this n-vector's datum (L{Datum}).
|
|
463
|
+
'''
|
|
464
|
+
return self._datum
|
|
465
|
+
|
|
466
|
+
@property_RO
|
|
467
|
+
def ellipsoidalNvector(self):
|
|
468
|
+
'''Get this C{Nvector}'s ellipsoidal class.
|
|
469
|
+
'''
|
|
470
|
+
return type(self)
|
|
471
|
+
|
|
472
|
+
def toCartesian(self, **Cartesian_and_kwds): # PYCHOK Cartesian=Cartesian
|
|
473
|
+
'''Convert this n-vector to C{Nvector}-based cartesian (ECEF) coordinates.
|
|
474
|
+
|
|
475
|
+
@kwarg Cartesian_and_kwds: Optional L{Cartesian}, B{C{h}}, B{C{datum}} and
|
|
476
|
+
other keyword arguments. Use C{B{Cartesian}=...}
|
|
477
|
+
to override this L{Cartesian} class or specify
|
|
478
|
+
C{B{Cartesian} is None}.
|
|
479
|
+
|
|
480
|
+
@return: The cartesian point (L{Cartesian}) or if B{C{Cartesian}} is set
|
|
481
|
+
to C{None}, an L{Ecef9Tuple}C{(x, y, z, lat, lon, height, C, M,
|
|
482
|
+
datum)} with C{C} and C{M} if available.
|
|
483
|
+
|
|
484
|
+
@raise TypeError: Invalid B{C{Cartesian_and_kwds}}.
|
|
485
|
+
'''
|
|
486
|
+
kwds = _xkwds(Cartesian_and_kwds, h=self.h, Cartesian=Cartesian,
|
|
487
|
+
datum=self.datum)
|
|
488
|
+
return NvectorBase.toCartesian(self, **kwds) # class or .classof
|
|
489
|
+
|
|
490
|
+
def toLatLon(self, **LatLon_and_kwds): # PYCHOK height=None, LatLon=LatLon
|
|
491
|
+
'''Convert this n-vector to an C{Nvector}-based geodetic point.
|
|
492
|
+
|
|
493
|
+
@kwarg LatLon_and_kwds: Optional L{LatLon}, B{C{height}}, B{C{datum}}
|
|
494
|
+
and other keyword arguments. Use C{B{LatLon}=...}
|
|
495
|
+
to override this L{LatLon} class or specify
|
|
496
|
+
C{B{LatLon} is None}.
|
|
497
|
+
|
|
498
|
+
@return: The geodetic point (L{LatLon}) or if B{C{LatLon}} is set
|
|
499
|
+
to C{None}, an L{Ecef9Tuple}C{(x, y, z, lat, lon, height,
|
|
500
|
+
C, M, datum)} with C{C} and C{M} if available.
|
|
501
|
+
|
|
502
|
+
@raise TypeError: Invalid B{C{LatLon_and_kwds}}.
|
|
503
|
+
'''
|
|
504
|
+
kwds = _xkwds(LatLon_and_kwds, height=self.h, datum=self.datum, LatLon=LatLon)
|
|
505
|
+
return NvectorBase.toLatLon(self, **kwds) # class or .classof
|
|
506
|
+
|
|
507
|
+
def unit(self, ll=None):
|
|
508
|
+
'''Normalize this vector to unit length.
|
|
509
|
+
|
|
510
|
+
@kwarg ll: Optional, original latlon (C{LatLon}).
|
|
511
|
+
|
|
512
|
+
@return: Normalised vector (C{Nvector}).
|
|
513
|
+
'''
|
|
514
|
+
u = NvectorBase.unit(self, ll=ll)
|
|
515
|
+
if u.datum != self.datum:
|
|
516
|
+
u._update(False, datum=self.datum)
|
|
517
|
+
return u
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def _Ecef():
|
|
521
|
+
# return the Ecef class and overwrite property_RO
|
|
522
|
+
Cartesian.Ecef = LatLon.Ecef = E = _MODS.ecef.EcefVeness
|
|
523
|
+
return E
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def meanOf(points, datum=_WGS84, height=None, wrap=False,
|
|
527
|
+
**LatLon_and_kwds):
|
|
528
|
+
'''Compute the geographic mean of several points.
|
|
529
|
+
|
|
530
|
+
@arg points: Points to be averaged (L{LatLon}[]).
|
|
531
|
+
@kwarg datum: Optional datum to use (L{Datum}).
|
|
532
|
+
@kwarg height: Optional height at mean point, overriding
|
|
533
|
+
the mean height (C{meter}).
|
|
534
|
+
@kwarg wrap: If C{True}, wrap or I{normalize} B{C{points}}
|
|
535
|
+
(C{bool}).
|
|
536
|
+
@kwarg LatLon_and_kwds: Optional B{C{LatLon}} class to return
|
|
537
|
+
the mean points and overriding this L{LatLon}
|
|
538
|
+
(or C{None}) and additional B{C{LatLon}}
|
|
539
|
+
keyword arguments, ignored if C{B{LatLon}
|
|
540
|
+
is None}.
|
|
541
|
+
|
|
542
|
+
@return: Geographic mean point and mean height (B{C{LatLon}})
|
|
543
|
+
or if B{C{LatLon}} is C{None}, an L{Ecef9Tuple}C{(x,
|
|
544
|
+
y, z, lat, lon, height, C, M, datum)} with C{C} and
|
|
545
|
+
C{M} if available.
|
|
546
|
+
|
|
547
|
+
@raise ValueError: Insufficient number of B{C{points}}.
|
|
548
|
+
'''
|
|
549
|
+
Ps = _Nvll.PointsIter(points, wrap=wrap)
|
|
550
|
+
# geographic mean
|
|
551
|
+
m = sumOf(p._N_vector for p in Ps.iterate(closed=False))
|
|
552
|
+
kwds = _xkwds(LatLon_and_kwds, height=height, datum=datum,
|
|
553
|
+
LatLon=LatLon, name=meanOf.__name__)
|
|
554
|
+
return m.toLatLon(**kwds)
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
def nearestOn(point, point1, point2, within=True, height=None, wrap=False,
|
|
558
|
+
equidistant=None, tol=_TOL_M, LatLon=LatLon, **LatLon_kwds):
|
|
559
|
+
'''I{Iteratively} locate the closest point on the geodesic between
|
|
560
|
+
two other (ellipsoidal) points.
|
|
561
|
+
|
|
562
|
+
@arg point: Reference point (C{LatLon}).
|
|
563
|
+
@arg point1: Start point of the geodesic (C{LatLon}).
|
|
564
|
+
@arg point2: End point of the geodesic (C{LatLon}).
|
|
565
|
+
@kwarg within: If C{True} return the closest point I{between}
|
|
566
|
+
B{C{point1}} and B{C{point2}}, otherwise the
|
|
567
|
+
closest point elsewhere on the geodesic (C{bool}).
|
|
568
|
+
@kwarg height: Optional height for the closest point (C{meter},
|
|
569
|
+
conventionally) or C{None} or C{False} for the
|
|
570
|
+
interpolated height. If C{False}, the closest
|
|
571
|
+
takes the heights of the points into account.
|
|
572
|
+
@kwarg wrap: If C{True}, wrap or I{normalize} and unroll I{only}
|
|
573
|
+
B{C{point1}} and B{C{point2}} (C{bool}).
|
|
574
|
+
@kwarg equidistant: An azimuthal equidistant projection (I{class}
|
|
575
|
+
or function L{pygeodesy.equidistant}) or C{None}
|
|
576
|
+
for the preferred C{B{point}.Equidistant}.
|
|
577
|
+
@kwarg tol: Convergence tolerance (C{meter}).
|
|
578
|
+
@kwarg LatLon: Optional class to return the closest point
|
|
579
|
+
(L{LatLon}) or C{None}.
|
|
580
|
+
@kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword
|
|
581
|
+
arguments, ignored if C{B{LatLon} is None}.
|
|
582
|
+
|
|
583
|
+
@return: Closest point, a B{C{LatLon}} instance or if C{B{LatLon}
|
|
584
|
+
is None}, a L{LatLon4Tuple}C{(lat, lon, height, datum)}.
|
|
585
|
+
|
|
586
|
+
@raise ImportError: Package U{geographiclib
|
|
587
|
+
<https://PyPI.org/project/geographiclib>}
|
|
588
|
+
not installed or not found.
|
|
589
|
+
|
|
590
|
+
@raise TypeError: Invalid or non-ellipsoidal B{C{point}}, B{C{point1}}
|
|
591
|
+
or B{C{point2}} or invalid B{C{equidistant}}.
|
|
592
|
+
|
|
593
|
+
@raise ValueError: No convergence for the B{C{tol}}.
|
|
594
|
+
|
|
595
|
+
@see: U{The B{ellipsoidal} case<https://GIS.StackExchange.com/questions/48937/
|
|
596
|
+
calculating-intersection-of-two-circles>} and U{Karney's paper
|
|
597
|
+
<https://ArXiv.org/pdf/1102.1215.pdf>}, pp 20-21, section B{14. MARITIME
|
|
598
|
+
BOUNDARIES} for more details about the iteration algorithm.
|
|
599
|
+
'''
|
|
600
|
+
return _nearestOn(point, point1, point2, within=within, height=height, wrap=wrap,
|
|
601
|
+
equidistant=equidistant, tol=tol, LatLon=LatLon, **LatLon_kwds)
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
def sumOf(nvectors, Vector=Nvector, h=None, **Vector_kwds):
|
|
605
|
+
'''Return the vectorial sum of two or more n-vectors.
|
|
606
|
+
|
|
607
|
+
@arg nvectors: Vectors to be added (C{Nvector}[]).
|
|
608
|
+
@kwarg Vector: Optional class for the vectorial sum (C{Nvector}).
|
|
609
|
+
@kwarg h: Optional height, overriding the mean height (C{meter}).
|
|
610
|
+
@kwarg Vector_kwds: Optional, additional B{C{Vector}} keyword
|
|
611
|
+
arguments, ignored if C{B{Vector} is None}.
|
|
612
|
+
|
|
613
|
+
@return: Vectorial sum (B{C{Vector}}).
|
|
614
|
+
|
|
615
|
+
@raise VectorError: No B{C{nvectors}}.
|
|
616
|
+
'''
|
|
617
|
+
return _sumOf(nvectors, Vector=Vector, h=h, **Vector_kwds)
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
@deprecated_function
|
|
621
|
+
def toNed(distance, bearing, elevation, Ned=Ned, name=NN):
|
|
622
|
+
'''DEPRECATED, use L{pygeodesy.Aer}C{(bearing, elevation,
|
|
623
|
+
distance).xyzLocal.toNed(B{Ned}, name=B{name})} or
|
|
624
|
+
L{XyzLocal}C{(pygeodesy.Aer(bearing, elevation,
|
|
625
|
+
distance)).toNed(B{Ned}, name=B{name})}.
|
|
626
|
+
|
|
627
|
+
Create an NED vector from distance, bearing and elevation
|
|
628
|
+
(in local coordinate system).
|
|
629
|
+
|
|
630
|
+
@arg distance: NED vector length (C{meter}).
|
|
631
|
+
@arg bearing: NED vector bearing (compass C{degrees360}).
|
|
632
|
+
@arg elevation: NED vector elevation from local coordinate
|
|
633
|
+
frame horizontal (C{degrees}).
|
|
634
|
+
@kwarg Ned: Optional class to return the NED (C{Ned}) or
|
|
635
|
+
C{None}.
|
|
636
|
+
@kwarg name: Optional name (C{str}).
|
|
637
|
+
|
|
638
|
+
@return: An NED vector equivalent to this B{C{distance}},
|
|
639
|
+
B{C{bearing}} and B{C{elevation}} (DEPRECATED L{Ned})
|
|
640
|
+
or a DEPRECATED L{Ned3Tuple}C{(north, east, down)}
|
|
641
|
+
if C{B{Ned} is None}.
|
|
642
|
+
|
|
643
|
+
@raise ValueError: Invalid B{C{distance}}, B{C{bearing}}
|
|
644
|
+
or B{C{elevation}}.
|
|
645
|
+
'''
|
|
646
|
+
if True: # use new Aer class
|
|
647
|
+
n, e, d, _ = _Aer(bearing, elevation, distance).xyz4
|
|
648
|
+
else: # DEPRECATED
|
|
649
|
+
d = Distance(distance)
|
|
650
|
+
|
|
651
|
+
sb, cb, se, ce = sincos2d_(Bearing(bearing),
|
|
652
|
+
Height(elevation=elevation))
|
|
653
|
+
n = cb * d * ce
|
|
654
|
+
e = sb * d * ce
|
|
655
|
+
d *= se
|
|
656
|
+
|
|
657
|
+
r = _MODS.deprecated.classes.Ned3Tuple(n, e, -d) if Ned is None else \
|
|
658
|
+
Ned(n, e, -d)
|
|
659
|
+
return _xnamed(r, name)
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
__all__ += _ALL_OTHER(Cartesian, LatLon, Ned, Nvector, # classes
|
|
663
|
+
meanOf, sumOf, toNed)
|
|
664
|
+
|
|
665
|
+
# **) MIT License
|
|
666
|
+
#
|
|
667
|
+
# Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
|
|
668
|
+
#
|
|
669
|
+
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
670
|
+
# copy of this software and associated documentation files (the "Software"),
|
|
671
|
+
# to deal in the Software without restriction, including without limitation
|
|
672
|
+
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
673
|
+
# and/or sell copies of the Software, and to permit persons to whom the
|
|
674
|
+
# Software is furnished to do so, subject to the following conditions:
|
|
675
|
+
#
|
|
676
|
+
# The above copyright notice and this permission notice shall be included
|
|
677
|
+
# in all copies or substantial portions of the Software.
|
|
678
|
+
#
|
|
679
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
680
|
+
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
681
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
682
|
+
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
683
|
+
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
684
|
+
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
685
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|