Coverage for pygeodesy/ltpTuples.py: 94%
515 statements
« prev ^ index » next coverage.py v7.2.2, created at 2023-08-12 12:31 -0400
« prev ^ index » next coverage.py v7.2.2, created at 2023-08-12 12:31 -0400
2# -*- coding: utf-8 -*-
4u'''Named, I{Local Tangent Plane} (LTP) tuples.
6Local coordinate classes L{XyzLocal}, L{Enu}, L{Ned} and L{Aer}
7and local coordinate tuples L{Local9Tuple}, L{Xyz4Tuple}, L{Enu4Tuple},
8L{Ned4Tuple}, L{Aer4Tuple}, L{ChLV9Tuple}, L{ChLVEN2Tuple},
9L{ChLVYX2Tuple}, L{ChLVyx2Tuple} and L{Footprint5Tuple}.
11@see: References in module L{ltp}.
12'''
14from pygeodesy.basics import isscalar, issubclassof
15from pygeodesy.constants import _0_0, _90_0, _N_90_0
16from pygeodesy.dms import F_D, toDMS
17from pygeodesy.errors import _TypeError, _TypesError, _xattr, _xkwds
18from pygeodesy.fmath import hypot, hypot_
19from pygeodesy.interns import NN, _4_, _azimuth_, _center_, _COMMASPACE_, \
20 _down_, _east_, _ecef_, _elevation_, _height_, \
21 _lat_, _lon_, _ltp_, _M_, _north_, _not_, _up_, \
22 _X_, _x_, _xyz_, _Y_, _y_, _z_
23from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS
24from pygeodesy.named import _NamedBase, _NamedTuple, notOverloaded, \
25 _Pass, _xnamed
26from pygeodesy.namedTuples import LatLon2Tuple, PhiLam2Tuple, Vector3Tuple
27from pygeodesy.props import deprecated_method, deprecated_Property_RO, \
28 Property_RO, property_RO
29from pygeodesy.streprs import Fmt, fstr, strs, _xzipairs
30from pygeodesy.units import Bearing, Degrees, Degrees_, Height, Lat, Lon, \
31 Meter, Meter_
32from pygeodesy.utily import atan2d, atan2b, sincos2d_
33from pygeodesy.vector3d import Vector3d
35from math import cos, radians
37__all__ = _ALL_LAZY.ltpTuples
38__version__ = '23.08.06'
40_aer_ = 'aer'
41_alt_ = 'alt'
42_enu_ = 'enu'
43_h__ = 'h_'
44_ned_ = 'ned'
45_local_ = 'local'
46_roll_ = 'roll'
47_slantrange_ = 'slantrange'
48_tilt_ = 'tilt'
49_yaw_ = 'yaw'
52def _er2gr(e, r):
53 '''(INTERNAL) Elevation and slant range to ground range.
54 '''
55 c = cos(radians(e))
56 return Meter_(groundrange=r * c)
59def _toStr2(inst, prec=None, fmt=Fmt.SQUARE, sep=_COMMASPACE_):
60 '''(INTERNAL) Get attribute name and value strings, joined and bracketed.
61 '''
62 a = inst._toStr # 'aer', 'enu', 'ned', 'xyz'
63 t = getattr(inst, a + _4_)[:len(a)]
64 t = strs(t, prec=3 if prec is None else prec)
65 if sep:
66 t = sep.join(t)
67 if fmt:
68 t = fmt(t)
69 return a, t
72def _4Tuple2Cls(inst, Cls, Cls_kwds):
73 '''(INTERNAL) Convert 4-Tuple to C{Cls} instance.
74 '''
75 if Cls is None:
76 return inst
77 elif issubclassof(Cls, Aer):
78 return inst.xyzLocal.toAer(Aer=Cls, **Cls_kwds)
79 elif issubclassof(Cls, Enu): # PYCHOK no cover
80 return inst.xyzLocal.toEnu(Enu=Cls, **Cls_kwds)
81 elif issubclassof(Cls, Ned):
82 return inst.xyzLocal.toNed(Ned=Cls, **Cls_kwds)
83 elif issubclassof(Cls, XyzLocal): # PYCHOK no cover
84 return inst.xyzLocal.toXyz(Xyz=Cls, **Cls_kwds)
85 elif Cls is Local9Tuple: # PYCHOK no cover
86 return inst.xyzLocal.toLocal9Tuple(**Cls_kwds)
87 n = inst.__class__.__name__[:3] # PYCHOK no cover
88 raise _TypesError(n, Cls, Aer, Enu, Ned, XyzLocal)
91def _xyz2aer4(inst):
92 '''(INTERNAL) Convert C{(x, y, z}) to C{(A, E, R)}.
93 '''
94 x, y, z, _ = inst.xyz4
95 A = Bearing(azimuth=atan2b(x, y))
96 E = Degrees(elevation=atan2d(z, hypot(x, y)))
97 R = Meter(slantrange=hypot_(x, y, z))
98 return Aer4Tuple(A, E, R, inst.ltp, name=inst.name)
101def _xyzLocal(*Types, **name_inst):
102 '''(INTERNAL) Get C{inst} or C{inst.xyzLocal}.
103 '''
104 n, inst = name_inst.popitem()
105 if isinstance(inst, Types):
106 return None
107 try:
108 return inst.xyzLocal
109 except (AttributeError, TypeError):
110 raise _TypeError(n, inst, txt=_not_(_local_))
113class _NamedAerNed(_NamedBase):
114 '''(INTERNAL) Base class for classes C{Aer} and C{Ned}.
115 '''
116 _ltp = None # local tangent plane (C{Ltp}), origin
118 @Property_RO
119 def ltp(self):
120 '''Get the I{local tangent plane} (L{Ltp}).
121 '''
122 return self._ltp
124 def toAer(self, Aer=None, **Aer_kwds):
125 '''Get the I{local} I{Azimuth, Elevation, slant Range} (AER) components.
127 @kwarg Aer: Class to return AER (L{Aer}) or C{None}.
128 @kwarg Aer_kwds: Optional, additional B{L{Aer}} keyword
129 arguments, ignored if B{C{Aer}} is C{None}.
131 @return: AER as an L{Aer} instance or if C{B{Aer} is None},
132 an L{Aer4Tuple}C{(azimuth, elevation, slantrange, ltp)}.
133 '''
134 return self.xyz4._toXyz(Aer, Aer_kwds)
136 def toEnu(self, Enu=None, **Enu_kwds):
137 '''Get the I{local} I{East, North, Up} (ENU) components.
139 @kwarg Enu: Class to return ENU (L{Enu}) or C{None}.
140 @kwarg Enu_kwds: Optional, additional B{L{Enu}} keyword
141 arguments, ignored if C{B{Enu} is None}.
143 @return: ENU as an L{Enu} instance or if C{B{Enu} is None},
144 an L{Enu4Tuple}C{(east, north, up, ltp)}.
145 '''
146 return self.xyz4._toXyz(Enu, Enu_kwds)
148 def toNed(self, Ned=None, **Ned_kwds):
149 '''Get the I{local} I{North, East, Down} (NED) components.
151 @kwarg Ned: Class to return NED (L{Ned}) or C{None}.
152 @kwarg Ned_kwds: Optional, additional B{L{Ned}} keyword
153 arguments, ignored if B{C{Ned}} is C{None}.
155 @return: NED as an L{Ned} instance or if C{B{Ned} is None},
156 an L{Ned4Tuple}C{(north, east, down, ltp)}.
157 '''
158 return self.xyz4._toXyz(Ned, Ned_kwds)
160 def toXyz(self, Xyz=None, **Xyz_kwds):
161 '''Get the local I{X, Y, Z} (XYZ) components.
163 @kwarg Xyz: Class to return XYZ (L{XyzLocal}, L{Enu},
164 L{Ned}, L{Aer}) or C{None}.
165 @kwarg Xyz_kwds: Optional, additional B{C{Xyz}} keyword
166 arguments, ignored if C{B{Xyz} is None}.
168 @return: XYZ as an B{C{Xyz}} instance or if C{B{Xyz} is None},
169 an L{Xyz4Tuple}C{(x, y, z, ltp)}.
171 @raise TypeError: Invalid B{C{Xyz}}.
172 '''
173 return self.xyz4._toXyz(Xyz, Xyz_kwds)
175 @Property_RO
176 def xyz(self):
177 '''Get the I{local} C{(X, Y, Z)} coordinates (L{Vector3Tuple}C{(x, y, z)}).
178 '''
179 return Vector3Tuple(self.x, self.y, self.z, name=self.name) # like Ecef9Tuple.xyz, Local6tuple.xyz
181 @property_RO
182 def xyz4(self): # PYCHOK no cover
183 '''(INTERNAL) I{Must be overloaded}, see function C{notOverloaded}.
184 '''
185 notOverloaded(self)
187 @Property_RO
188 def xyzLocal(self):
189 '''Get this AER or NED as an L{XyzLocal}.
190 '''
191 return XyzLocal(self.xyz4, name=self.name)
194class Aer(_NamedAerNed):
195 '''Local C{Azimuth-Elevation-Range} (AER) in a I{local tangent plane}.
196 '''
197 _azimuth = _0_0 # bearing from North (C{degrees360})
198 _elevation = _0_0 # tilt, pitch from horizon (C{degrees}).
199# _ltp = None # local tangent plane (C{Ltp}), origin
200 _slantrange = _0_0 # distance (C{Meter})
201 _toStr = _aer_
203 def __init__(self, azimuth_aer, elevation=0, slantrange=0, ltp=None, name=NN):
204 '''New L{Aer}.
206 @arg azimuth_aer: Scalar azimuth, bearing from North (C{degrees360})
207 or a previous I{local} instance (L{Aer}, L{Aer4Tuple},
208 L{Enu}, L{Enu4Tuple}, L{Local9Tuple}, L{Ned},
209 L{Ned4Tuple}, L{XyzLocal} or L{Xyz4Tuple}).
210 @kwarg elevation: Scalar angle I{above} horizon, I{above} B{C{ltp}}
211 (C{degrees90}) only used with scalar B{C{azimuth_aer}}.
212 @kwarg slantrange: Scalar distance (C{meter}), only used with scalar
213 B{C{azimuth_aer}}.
214 @kwarg ltp: The I{local tangent plane}, (geodetic) origin (L{Ltp},
215 L{LocalCartesian}).
216 @kwarg name: Optional name (C{str}).
218 @raise TypeError: Invalid B{C{azimuth_aer}} or B{C{ltp}}.
220 @raise UnitError: Invalid B{C{azimuth_aer}}, B{C{elevation}} or
221 or B{C{slantrange}}.
222 '''
223 if isscalar(azimuth_aer):
224 self._azimuth = Bearing(azimuth=azimuth_aer)
225 self._elevation = Degrees_(elevation=elevation, low=_N_90_0, high=_90_0)
226 self._slantrange = Meter_(slantrange=slantrange)
227 p, n = ltp, name
228 else: # PYCHOK no cover
229 p = _xyzLocal(Aer, Aer4Tuple, Ned, azimuth_aer=azimuth_aer)
230 aer = p.toAer() if p else azimuth_aer
231 self._azimuth, self._elevation, self._slantrange = \
232 aer.azimuth, aer.elevation, aer.slantrange
233 p = _xattr(aer, ltp=ltp)
234 n = name or _xattr(aer, name=name)
236 if p:
237 self._ltp = _MODS.ltp._xLtp(p)
238 if name:
239 self.name = n
241 @Property_RO
242 def aer4(self):
243 '''Get the C{(azimuth, elevation, slantrange, ltp)} components (L{Aer4Tuple}).
244 '''
245 return Aer4Tuple(self.azimuth, self.elevation, self.slantrange, self.ltp, name=self.name)
247 @Property_RO
248 def azimuth(self):
249 '''Get the Azimuth, bearing from North (C{degrees360}).
250 '''
251 return self._azimuth
253 @Property_RO
254 def down(self):
255 '''Get the Down component (C{meter}).
256 '''
257 return self.xyzLocal.down
259 @Property_RO
260 def east(self):
261 '''Get the East component (C{meter}).
262 '''
263 return self.xyzLocal.east
265 @Property_RO
266 def elevation(self):
267 '''Get the Elevation, tilt above horizon (C{degrees90}).
268 '''
269 return self._elevation
271 @Property_RO
272 def groundrange(self):
273 '''Get the I{ground range}, distance (C{meter}).
274 '''
275 return _er2gr(self._elevation, self._slantrange)
277 @Property_RO
278 def north(self):
279 '''Get the North component (C{meter}).
280 '''
281 return self.xyzLocal.north
283 @Property_RO
284 def slantrange(self):
285 '''Get the I{slant Range}, distance (C{meter}).
286 '''
287 return self._slantrange
289 def toRepr(self, prec=None, fmt=Fmt.SQUARE, sep=_COMMASPACE_, **unused): # PYCHOK expected
290 '''Return a string representation of this AER as azimuth
291 (bearing), elevation and slant range.
293 @kwarg prec: Number of (decimal) digits, unstripped (C{int}).
294 @kwarg fmt: Enclosing backets format (C{str}).
295 @kwarg sep: Optional separator between AERs (C{str}).
297 @return: This AER as "[A:degrees360, E:degrees90, R:meter]" (C{str}).
298 '''
299 t = (toDMS(self.azimuth, form=F_D, prec=prec, ddd=0),
300 toDMS(self.elevation, form=F_D, prec=prec, ddd=0),
301 fstr( self.slantrange, prec=3 if prec is None else prec))
302 return _xzipairs(self._toStr.upper(), t, sep=sep, fmt=fmt)
304 def toStr(self, **prec_fmt_sep): # PYCHOK expected
305 '''Return a string representation of this AER.
307 @kwarg prec_fmt_sep: Keyword arguments C{B{prec}=3} for the
308 number of (decimal) digits, unstripped
309 (C{int}), C{B{fmt}='[]'} the enclosing
310 backets format (C{str}) and separator
311 C{B{sep}=', '} to join (C{str}).
313 @return: This AER as "[degrees360, degrees90, meter]" (C{str}).
314 '''
315 _, t = _toStr2(self, **prec_fmt_sep)
316 return t
318 @Property_RO
319 def up(self):
320 '''Get the Up component (C{meter}).
321 '''
322 return self.xyzLocal.up
324 @Property_RO
325 def x(self):
326 '''Get the X component (C{meter}).
327 '''
328 return self.xyz4.x
330 @Property_RO
331 def xyz4(self):
332 '''Get the C{(x, y, z, ltp)} components (L{Xyz4Tuple}).
333 '''
334 sA, cA, sE, cE = sincos2d_(self._azimuth, self._elevation)
335 R = self._slantrange
336 r = cE * R # ground range
337 return Xyz4Tuple(sA * r, cA * r, sE * R, self.ltp, name=self.name)
339 @Property_RO
340 def y(self):
341 '''Get the Y component (C{meter}).
342 '''
343 return self.xyz4.y
345 @Property_RO
346 def z(self):
347 '''Get the Z component (C{meter}).
348 '''
349 return self.xyz4.z
352class Aer4Tuple(_NamedTuple):
353 '''4-Tuple C{(azimuth, elevation, slantrange, ltp)},
354 all in C{meter} except C{ltp}.
355 '''
356 _Names_ = (_azimuth_, _elevation_, _slantrange_, _ltp_)
357 _Units_ = ( Meter, Meter, Meter, _Pass)
359 def _toAer(self, Cls, Cls_kwds):
360 '''(INTERNAL) Return C{Cls(..., **Cls_kwds)} instance.
361 '''
362 if issubclassof(Cls, Aer):
363 return Cls(*self, **_xkwds(Cls_kwds, name=self.name))
364 else:
365 return _4Tuple2Cls(self, Cls, Cls_kwds)
367 @Property_RO
368 def groundrange(self):
369 '''Get the I{ground range}, distance (C{meter}).
370 '''
371 return _er2gr(self.elevation, self.slantrange) # PYCHOK _Tuple
373 @Property_RO
374 def xyzLocal(self):
375 '''Get this L{Aer4Tuple} as an L{XyzLocal}.
376 '''
377 return Aer(self).xyzLocal
380class Attitude4Tuple(_NamedTuple):
381 '''4-Tuple C{(alt, tilt, yaw, roll)} with C{altitude} in (positive)
382 C{meter} and C{tilt}, C{yaw} and C{roll} in C{degrees} representing
383 the attitude of a plane or camera.
384 '''
385 _Names_ = (_alt_, _tilt_, _yaw_, _roll_)
386 _Units_ = ( Meter, Bearing, Degrees, Degrees)
388 @Property_RO
389 def atyr(self):
390 '''Return this attitude (L{Attitude4Tuple}).
391 '''
392 return self
394 @Property_RO
395 def tyr3d(self):
396 '''Get this attitude's (3-D) directional vector (L{Vector3d}).
397 '''
398 return _MODS.ltp.Attitude(self).tyr3d
401class Ned(_NamedAerNed):
402 '''Local C{North-Eeast-Down} (NED) location in a I{local tangent plane}.
404 @see: L{Enu} and L{Ltp}.
405 '''
406 _down = _0_0 # down, -XyzLocal.z (C{meter}).
407 _east = _0_0 # east, XyzLocal.y (C{meter}).
408# _ltp = None # local tangent plane (C{Ltp}), origin
409 _north = _0_0 # north, XyzLocal.x (C{meter})
410 _toStr = _ned_
412 def __init__(self, north_ned, east=0, down=0, ltp=None, name=NN):
413 '''New L{Ned} vector.
415 @arg north_ned: Scalar North component (C{meter}) or a previous
416 I{local} instance (L{Ned}, L{Ned4Tuple}, L{Aer},
417 L{Aer4Tuple}, L{Enu}, L{Enu4Tuple}, L{Local9Tuple},
418 L{XyzLocal} or L{Xyz4Tuple}).
419 @kwarg east: Scalar East component (C{meter}), only used with
420 scalar B{C{north_ned}}.
421 @kwarg down: Scalar Down component, normal to I{inside} surface
422 of the ellipsoid or sphere (C{meter}), only used with
423 scalar B{C{north_ned}}.
424 @kwarg ltp: The I{local tangent plane}, (geodetic) origin (L{Ltp},
425 L{LocalCartesian}).
426 @kwarg name: Optional name (C{str}).
428 @raise TypeError: Invalid B{C{north_ned}} or B{C{ltp}}.
430 @raise UnitError: Invalid B{C{north_ned}}, B{C{east}} or B{C{down}}.
431 '''
432 if isscalar(north_ned):
433 self._north = Meter(north=north_ned or _0_0)
434 self._east = Meter(east=east or _0_0)
435 self._down = Meter(down=down or _0_0)
436 p, n = ltp, name
437 else: # PYCHOK no cover
438 p = _xyzLocal(Ned, Ned4Tuple, Aer, north_ned=north_ned)
439 ned = p.toNed() if p else north_ned
440 self._north, self._east, self._down = ned.north, ned.east, ned.down
441 p = _xattr(ned, ltp=ltp)
442 n = name or _xattr(ned, name=name)
444 if p:
445 self._ltp = _MODS.ltp._xLtp(p)
446 if n:
447 self.name = n
449 @Property_RO
450 def aer4(self):
451 '''Get the C{(azimuth, elevation, slantrange, ltp)} components (L{Aer4Tuple}).
452 '''
453 return _xyz2aer4(self)
455 @Property_RO
456 def azimuth(self):
457 '''Get the Azimuth, bearing from North (C{degrees360}).
458 '''
459 return self.aer4.azimuth
461 @deprecated_Property_RO
462 def bearing(self):
463 '''DEPRECATED, use C{azimuth}.'''
464 return self.azimuth
466 @Property_RO
467 def down(self):
468 '''Get the Down component (C{meter}).
469 '''
470 return self._down
472 @Property_RO
473 def east(self):
474 '''Get the East component (C{meter}).
475 '''
476 return self._east
478 @Property_RO
479 def elevation(self):
480 '''Get the Elevation, tilt above horizon (C{degrees90}).
481 '''
482 return self.aer4.elevation # neg(degrees90(asin1(self.down / self.length))))
484 @Property_RO
485 def groundrange(self):
486 '''Get the I{ground range}, distance (C{meter}).
487 '''
488 return Meter(groundrange=hypot(self.north, self.east))
490 @deprecated_Property_RO
491 def length(self):
492 '''DEPRECATED, use C{slantrange}.'''
493 return self.slantrange
495 @deprecated_Property_RO
496 def ned(self):
497 '''DEPRECATED, use property C{ned4}.'''
498 return _MODS.deprecated.Ned3Tuple(self.north, self.east, self.down, name=self.name)
500 @Property_RO
501 def ned4(self):
502 '''Get the C{(north, east, down, ltp)} components (L{Ned4Tuple}).
503 '''
504 return Ned4Tuple(self.north, self.east, self.down, self.ltp, name=self.name)
506 @Property_RO
507 def north(self):
508 '''Get the North component (C{meter}).
509 '''
510 return self._north
512 @Property_RO
513 def slantrange(self):
514 '''Get the I{slant Range}, distance (C{meter}).
515 '''
516 return self.aer4.slantrange
518 @deprecated_method
519 def to3ned(self): # PYCHOK no cover
520 '''DEPRECATED, use property L{ned4}.'''
521 return self.ned # XXX deprecated too
523 def toRepr(self, prec=None, fmt=Fmt.SQUARE, sep=_COMMASPACE_, **unused): # PYCHOK expected
524 '''Return a string representation of this NED.
526 @kwarg prec: Number of (decimal) digits, unstripped (C{int}).
527 @kwarg fmt: Enclosing backets format (C{str}).
528 @kwarg sep: Separator to join (C{str}).
530 @return: This NED as "[N:meter, E:meter, D:meter]" (C{str}).
531 '''
532 a, t = _toStr2(self, prec=prec, fmt=NN, sep=NN)
533 return _xzipairs(a.upper(), t, sep=sep, fmt=fmt)
535 def toStr(self, **prec_fmt_sep): # PYCHOK expected
536 '''Return a string representation of this NED.
538 @kwarg prec_fmt_sep: Keyword arguments C{B{prec}=3} for the
539 number of (decimal) digits, unstripped
540 (C{int}), C{B{fmt}='[]'} the enclosing
541 backets format (C{str}) and separator
542 C{B{sep}=', '} to join (C{str}).
544 @return: This NED as "[meter, meter, meter]" (C{str}).
545 '''
546 _, t = _toStr2(self, **prec_fmt_sep)
547 return t
549 @deprecated_method
550 def toVector3d(self):
551 '''DEPRECATED, use property L{xyz}.'''
552 return self.xyz
554 @Property_RO
555 def up(self):
556 '''Get the Up component (C{meter}).
557 '''
558 return Meter(up=-self._down) # negated
560 @Property_RO
561 def x(self):
562 '''Get the X component (C{meter}).
563 '''
564 return Meter(x=self._east) # 2nd arg, E
566 @Property_RO
567 def xyz4(self):
568 '''Get the C{(x, y, z, ltp)} components (L{Xyz4Tuple}).
569 '''
570 return Xyz4Tuple(self.x, self.y, self.z, self.ltp, name=self.name)
572 @Property_RO
573 def y(self):
574 '''Get the Y component (C{meter}).
575 '''
576 return Meter(y=self._north) # 1st arg N
578 @Property_RO
579 def z(self):
580 '''Get the Z component (C{meter}).
581 '''
582 return Meter(z=-self._down) # negated
585class Ned4Tuple(_NamedTuple):
586 '''4-Tuple C{(north, east, down, ltp)}, all in C{meter} except C{ltp}.
587 '''
588 _Names_ = (_north_, _east_, _down_, _ltp_)
589 _Units_ = ( Meter, Meter, Meter, _Pass)
591 def _toNed(self, Cls, Cls_kwds):
592 '''(INTERNAL) Return C{Cls(..., **Cls_kwds)} instance.
593 '''
594 if issubclassof(Cls, Ned):
595 return Cls(*self, **_xkwds(Cls_kwds, name=self.name))
596 else:
597 return _4Tuple2Cls(self, Cls, Cls_kwds)
599 @Property_RO
600 def xyzLocal(self):
601 '''Get this L{Ned4Tuple} as an L{XyzLocal}.
602 '''
603 return Ned(self).xyzLocal
606class XyzLocal(Vector3d):
607 '''Local C{(x, y, z)} in a I{local tangent plane} (LTP),
608 also base class for local L{Enu}.
609 '''
610 _ltp = None # local tangent plane (C{Ltp}), origin
611 _toStr = _xyz_
613 def __init__(self, x_xyz, y=0, z=0, ltp=None, name=NN):
614 '''New L{XyzLocal}.
616 @arg x_xyz: Scalar X component (C{meter}), C{positive east} or a
617 previous I{local} instance (L{XyzLocal}, L{Xyz4Tuple},
618 L{Aer}, L{Aer4Tuple}, L{Enu}, L{Enu4Tuple},
619 L{Local9Tuple}, L{Ned} or L{Ned4Tuple}).
620 @kwarg y: Scalar Y component (C{meter}), only used with scalar
621 B{C{x_xyz}}, C{positive north}.
622 @kwarg z: Scalar Z component, normal C{positive up} from the
623 surface of the ellipsoid or sphere (C{meter}), only
624 used with scalar B{C{x_xyz}}.
625 @kwarg ltp: The I{local tangent plane}, (geodetic) origin (L{Ltp},
626 L{LocalCartesian}).
628 @raise TypeError: Invalid B{C{x_xyz}} or B{C{ltp}}.
630 @raise UnitError: Invalid scalar B{C{x_xyz}}, B{C{y}} or B{C{z}}.
631 '''
632 if isscalar(x_xyz):
633 self._x = Meter(x=x_xyz or _0_0)
634 self._y = Meter(y=y or _0_0)
635 self._z = Meter(z=z or _0_0)
636 p, n = ltp, name
637 else:
638 xyz = _xyzLocal(XyzLocal, Xyz4Tuple, Local9Tuple, x_xyz=x_xyz) or x_xyz
639 self._x, self._y, self._z = xyz.x, xyz.y, xyz.z
640 p = _xattr(xyz, ltp=ltp)
641 n = name or _xattr(xyz, name=NN)
643 if p:
644 self._ltp = _MODS.ltp._xLtp(p)
645 if n:
646 self.name = n
648 def __str__(self):
649 return self.toStr()
651 @Property_RO
652 def aer4(self):
653 '''Get the C{(azimuth, elevation, slantrange, ltp)} components (L{Aer4Tuple}).
654 '''
655 return _xyz2aer4(self)
657 @Property_RO
658 def azimuth(self):
659 '''Get the Azimuth, bearing from North (C{degrees360}).
661 @see: U{Azimuth<https://GSSC.ESA.int/navipedia/index.php/
662 Transformations_between_ECEF_and_ENU_coordinates>}.
663 '''
664 return self.aer4.azimuth
666 def classof(self, *args, **kwds): # PYCHOK no cover
667 '''Create another instance of this very class.
669 @arg args: Optional, positional arguments.
670 @kwarg kwds: Optional, keyword arguments.
672 @return: New instance (C{self.__class__}).
673 '''
674 kwds = _xkwds(kwds, ltp=self.ltp, name=self.name)
675 return self.__class__(*args, **kwds)
677 @Property_RO
678 def down(self):
679 '''Get the Down component (C{meter}).
680 '''
681 return Meter(down=-self.z)
683 @property_RO
684 def ecef(self):
685 '''Get this LTP's ECEF converter (C{Ecef...} I{instance}).
686 '''
687 return self.ltp.ecef
689 @Property_RO
690 def east(self):
691 '''Get the East component (C{meter}).
692 '''
693 return Meter(east=self.x)
695 @Property_RO
696 def elevation(self):
697 '''Get the Elevation, tilt above horizon (C{degrees90}).
699 @see: U{Elevation<https://GSSC.ESA.int/navipedia/index.php/
700 Transformations_between_ECEF_and_ENU_coordinates>}.
701 '''
702 return self.aer4.elevation # neg(degrees90(asin1(self.down / self.length))))
704 @Property_RO
705 def enu4(self):
706 '''Get the C{(east, north, up, ltp)} components (L{Enu4Tuple}).
707 '''
708 return Enu4Tuple(self.east, self.north, self.up, self.ltp, name=self.name)
710 @Property_RO
711 def groundrange(self):
712 '''Get the I{ground range}, distance (C{meter}).
713 '''
714 return Meter(groundrange=hypot(self.x, self.y))
716 @Property_RO
717 def ltp(self):
718 '''Get the I{local tangent plane} (L{Ltp}).
719 '''
720 return self._ltp
722 @Property_RO
723 def ned4(self):
724 '''Get the C{(north, east, down, ltp)} components (L{Ned4Tuple}).
725 '''
726 return Ned4Tuple(self.north, self.east, self.down, self.ltp, name=self.name)
728 @Property_RO
729 def north(self):
730 '''Get the North component (C{meter}).
731 '''
732 return Meter(north=self.y)
734 @Property_RO
735 def slantrange(self):
736 '''Get the I{slant Range}, distance (C{meter}).
737 '''
738 return self.aer4.slantrange
740 def toAer(self, Aer=None, **Aer_kwds):
741 '''Get the local I{Azimuth, Elevation, slantRange} components.
743 @kwarg Aer: Class to return AER (L{Aer}) or C{None}.
744 @kwarg Aer_kwds: Optional, additional B{C{Aer}} keyword
745 arguments, ignored if C{B{Aer} is None}.
747 @return: AER as an L{Aer} instance or if C{B{Aer} is None}, an
748 L{Aer4Tuple}C{(azimuth, elevation, slantrange, ltp)}.
750 @raise TypeError: Invalid B{C{Aer}}.
751 '''
752 return self.aer4._toAer(Aer, Aer_kwds)
754 def toCartesian(self, Cartesian=None, ltp=None, **Cartesian_kwds):
755 '''Get the geocentric C{(x, y, z)} (ECEF) coordinates of this local.
757 @kwarg Cartesian: Optional class to return C{(x, y, z)} (C{Cartesian})
758 or C{None}.
759 @kwarg ltp: Optional I{local tangent plane} (LTP) (L{Ltp}),
760 overriding this C{ltp}.
761 @kwarg Cartesian_kwds: Optional, additional B{C{Cartesian}} keyword
762 arguments, ignored if C{B{Cartesian} is None}.
764 @return: A B{C{Cartesian}} instance of if C{B{Cartesian} is None}, an
765 L{Ecef9Tuple}C{(x, y, z, lat, lon, height, C, M, datum)}
766 with C{M=None}, always.
768 @raise TypeError: Invalid B{C{ltp}}, B{C{Cartesian}} or
769 B{C{Cartesian_kwds}} argument.
770 '''
771 ltp = _MODS.ltp._xLtp(ltp, self.ltp)
772 if Cartesian is None:
773 r = ltp._local2ecef(self, nine=True)
774 else:
775 x, y, z = ltp._local2ecef(self)
776 kwds = _xkwds(Cartesian_kwds, datum=ltp.datum)
777 r = Cartesian(x, y, z, **kwds)
778 return _xnamed(r, self.name or ltp.name)
780 def toEnu(self, Enu=None, **Enu_kwds):
781 '''Get the local I{East, North, Up} (ENU) components.
783 @kwarg Enu: Class to return ENU (L{Enu}) or C{None}.
784 @kwarg Enu_kwds: Optional, additional B{C{Enu}} keyword
785 arguments, ignored if C{B{Enu} is None}.
787 @return: ENU as an L{Enu} instance or if C{B{Enu} is None},
788 an L{Enu4Tuple}C{(east, north, up, ltp)}.
789 '''
790 return self.enu4._toEnu(Enu, Enu_kwds)
792 def toLatLon(self, LatLon=None, ltp=None, **LatLon_kwds):
793 '''Get the geodetic C{(lat, lon, height)} coordinates if this local.
795 @kwarg LatLon: Optional class to return C{(x, y, z)} (C{LatLon})
796 or C{None}.
797 @kwarg ltp: Optional I{local tangent plane} (LTP) (L{Ltp}),
798 overriding this ENU/NED/AER/XYZ's LTP.
799 @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword
800 arguments, ignored if C{B{LatLon} is None}.
802 @return: An B{C{LatLon}} instance of if C{B{LatLon} is None}, an
803 L{Ecef9Tuple}C{(x, y, z, lat, lon, height, C, M,
804 datum)} with C{M=None}, always.
806 @raise TypeError: Invalid B{C{ltp}}, B{C{LatLon}} or
807 B{C{LatLon_kwds}} argument.
808 '''
809 ltp = _MODS.ltp._xLtp(ltp, self.ltp)
810 r = ltp._local2ecef(self, nine=True)
811 if LatLon is None:
812 r = _xnamed(r, self.name or ltp.name)
813 else:
814 kwds = _xkwds(LatLon_kwds, height=r.height, datum=r.datum,
815 name=self.name or ltp.name)
816 r = LatLon(r.lat, r.lon, **kwds) # XXX ltp?
817 return r
819 def toLocal9Tuple(self, M=False, name=NN):
820 '''Get this local as a C{Local9Tuple}.
822 @kwarg M: Optionally include the rotation matrix (C{bool}).
823 @kwarg name: Optional name (C{str}).
825 @return: L{Local9Tuple}C{(x, y, z, lat, lon, height, ltp,
826 ecef, M)} with C{ltp} this C{Ltp}, C{ecef} an
827 L{Ecef9Tuple} and C{M} L{EcefMatrix} or C{None}.
828 '''
829 ltp = self.ltp # see C{self.toLatLon}
830 t = ltp._local2ecef(self, nine=True, M=M)
831 return Local9Tuple(self.x, self.y, self.z, t.lat, t.lon, t.height,
832 ltp, t, t.M, name=name or t.name)
834 def toNed(self, Ned=None, **Ned_kwds):
835 '''Get the local I{North, East, Down} (Ned) components.
837 @kwarg Ned: Class to return NED (L{Ned}) or C{None}.
838 @kwarg Ned_kwds: Optional, additional B{C{Ned}} keyword
839 arguments, ignored if C{B{Ned} is None}.
841 @return: NED as an L{Ned} instance or if C{B{Ned} is None},
842 an L{Ned4Tuple}C{(north, east, down, ltp)}.
843 '''
844 return self.ned4._toNed(Ned, Ned_kwds)
846 def toRepr(self, prec=None, fmt=Fmt.SQUARE, sep=_COMMASPACE_, **unused): # PYCHOK expected
847 '''Return a string representation of this ENU/NED/XYZ.
849 @kwarg prec: Number of (decimal) digits, unstripped (C{int}).
850 @kwarg fmt: Enclosing backets format (C{str}).
851 @kwarg sep: Separator to join (C{str}).
853 @return: This XYZ/ENU as "[E:meter, N:meter, U:meter]",
854 "[N:meter, E:meter, D:meter]" respectively
855 "[X:meter, Y:meter, Z:meter]" (C{str}).
856 '''
857 a, t = _toStr2(self, prec=prec, fmt=NN, sep=NN)
858 return _xzipairs(a.upper(), t, sep=sep, fmt=fmt)
860 def toStr(self, **prec_fmt_sep): # PYCHOK expected
861 '''Return a string representation of this XYZ.
863 @kwarg prec_fmt_sep: Keyword arguments C{B{prec}=3} for the
864 number of (decimal) digits, unstripped
865 (C{int}), C{B{fmt}='[]'} the enclosing
866 backets format (C{str}) and separator
867 C{B{sep}=', '} to join (C{str}).
869 @return: This XYZ as "[meter, meter, meter]" (C{str}).
870 '''
871 _, t = _toStr2(self, **prec_fmt_sep)
872 return t
874 def toXyz(self, Xyz=None, **Xyz_kwds):
875 '''Get the local I{X, Y, Z} (XYZ) components.
877 @kwarg Xyz: Class to return XYZ (L{XyzLocal}, L{Enu},
878 L{Ned}, L{Aer}) or C{None}.
879 @kwarg Xyz_kwds: Optional, additional B{C{Xyz}} keyword
880 arguments, ignored if C{B{Xyz} is None}.
882 @return: XYZ as an B{C{Xyz}} instance or if C{B{Xyz} is None},
883 an L{Xyz4Tuple}C{(x, y, z, ltp)}.
884 '''
885 return self.xyz4._toXyz(Xyz, Xyz_kwds)
887 @Property_RO
888 def up(self):
889 '''Get the Up component (C{meter}).
890 '''
891 return Meter(up=self.z)
893# @Property_RO
894# def x(self): # see: Vector3d.x
895# '''Get the X component (C{meter}).
896# '''
897# return self._x
899# @Property_RO
900# def xyz(self): # see: Vector3d.xyz
901# '''Get the I{local} C{(X, Y, Z)} coordinates (L{Vector3Tuple}C{(x, y, z)}).
902# '''
903# return Vector3Tuple(self.x, self.y, self.z, name=self.name) # like Ecef9Tuple.xyz, Local6tuple.xyz
905 @Property_RO
906 def xyz4(self):
907 '''Get the C{(x, y, z, ltp)} components (L{Xyz4Tuple}).
908 '''
909 return Xyz4Tuple(self.x, self.y, self.z, self.ltp, name=self.name)
911 @Property_RO
912 def xyzLocal(self):
913 '''Get this L{XyzLocal}.
914 '''
915 return self
917# @Property_RO
918# def y(self): # see: Vector3d.y
919# '''Get the Y component (C{meter}).
920# '''
921# return self._y
923# @Property_RO
924# def z(self): # see: Vector3d.z
925# '''Get the Z component (C{meter}).
926# '''
927# return self._z
930class Xyz4Tuple(_NamedTuple):
931 '''4-Tuple C{(x, y, z, ltp)}, all in C{meter} except C{ltp}.
932 '''
933 _Names_ = (_x_, _y_, _z_, _ltp_)
934 _Units_ = ( Meter, Meter, Meter, _Pass)
936 def _toXyz(self, Cls, Cls_kwds):
937 '''(INTERNAL) Return C{Cls(..., **Cls_kwds)} instance.
938 '''
939 if issubclassof(Cls, XyzLocal):
940 return Cls(*self, **_xkwds(Cls_kwds, name=self.name))
941 else:
942 return _4Tuple2Cls(self, Cls, Cls_kwds)
944 @Property_RO
945 def xyzLocal(self):
946 '''Get this L{Xyz4Tuple} as an L{XyzLocal}.
947 '''
948 return XyzLocal(*self, name=self.name)
951class Enu(XyzLocal):
952 '''Local C{Eeast-North-Up} (ENU) location in a I{local tangent plane}.
954 @see: U{East, North, Up (ENU)<https://GSSC.ESA.int/navipedia/index.php/
955 Transformations_between_ECEF_and_ENU_coordinates>} coordinates.
956 '''
957 _toStr = _enu_
959 def __init__(self, east_enu, north=0, up=0, ltp=None, name=NN):
960 '''New L{Enu}.
962 @arg east_enu: Scalar East component (C{meter}) or a previous
963 I{local} instance (L{Enu}, L{Enu4Tuple}, L{Aer},
964 L{Aer4Tuple}, L{Local9Tuple}, L{Ned}, L{Ned4Tuple},
965 L{XyzLocal} or L{Xyz4Tuple}).
966 @kwarg north: Scalar North component (C{meter}) only used with
967 scalar B{C{east_enu}}.
968 @kwarg up: Scalar Up component only used with scalar B{C{east_enu}},
969 normal from the surface of the ellipsoid or sphere (C{meter}).
970 @kwarg ltp: The I{local tangent plane}, (geodetic) origin (L{Ltp},
971 L{LocalCartesian}).
972 @kwarg name: Optional name (C{str}).
974 @raise TypeError: Invalid B{C{east_enu}} or B{C{ltp}}.
976 @raise UnitError: Invalid B{C{east_enu}}, B{C{north}} or B{C{up}}.
977 '''
978 XyzLocal.__init__(self, east_enu, north, up, ltp=ltp, name=name)
980 @Property_RO
981 def xyzLocal(self):
982 '''Get this ENU as an L{XyzLocal}.
983 '''
984 return XyzLocal(*self.xyz4, name=self.name)
987class Enu4Tuple(_NamedTuple):
988 '''4-Tuple C{(east, north, up, ltp)}, in C{meter} except C{ltp}.
989 '''
990 _Names_ = (_east_, _north_, _up_, _ltp_)
991 _Units_ = ( Meter, Meter, Meter, _Pass)
993 def _toEnu(self, Cls, Cls_kwds):
994 '''(INTERNAL) Return C{Cls(..., **Cls_kwds)} instance.
995 '''
996 if issubclassof(Cls, XyzLocal):
997 return Cls(*self, **_xkwds(Cls_kwds, name=self.name))
998 else:
999 return _4Tuple2Cls(self, Cls, Cls_kwds)
1001 @Property_RO
1002 def xyzLocal(self):
1003 '''Get this L{Enu4Tuple} as an L{XyzLocal}.
1004 '''
1005 return XyzLocal(*self, name=self.name)
1008class Local9Tuple(_NamedTuple):
1009 '''9-Tuple C{(x, y, z, lat, lon, height, ltp, ecef, M)} with I{local} C{x},
1010 C{y}, C{z} all in C{meter}, I{geodetic} C{lat}, C{lon}, C{height}, I{local
1011 tangent plane} C{ltp} (L{Ltp}), C{ecef} (L{Ecef9Tuple}) with I{geocentric}
1012 C{x}, C{y}, C{z}, I{geodetic} C{lat}, C{lon}, C{height} and I{concatenated}
1013 rotation matrix C{M} (L{EcefMatrix}) or C{None}.
1014 '''
1015 _Names_ = (_x_, _y_, _z_, _lat_, _lon_, _height_, _ltp_, _ecef_, _M_)
1016 _Units_ = ( Meter, Meter, Meter, Lat, Lon, Height, _Pass, _Pass, _Pass)
1018 @Property_RO
1019 def azimuth(self):
1020 '''Get the I{local} Azimuth, bearing from North (C{degrees360}).
1021 '''
1022 return self.xyzLocal.aer4.azimuth
1024 @Property_RO
1025 def down(self):
1026 '''Get the I{local} Down, C{-z} component (C{meter}).
1027 '''
1028 return -self.z
1030 @Property_RO
1031 def east(self):
1032 '''Get the I{local} East, C{x} component (C{meter}).
1033 '''
1034 return self.x
1036 @Property_RO
1037 def elevation(self):
1038 '''Get the I{local} Elevation, tilt I{above} horizon (C{degrees90}).
1039 '''
1040 return self.xyzLocal.aer4.elevation
1042 @Property_RO
1043 def groundrange(self):
1044 '''Get the I{local} ground range, distance (C{meter}).
1045 '''
1046 return self.xyzLocal.aer4.groundrange
1048 @Property_RO
1049 def lam(self):
1050 '''Get the I{geodetic} longitude in C{radians} (C{float}).
1051 '''
1052 return self.philam.lam
1054 @Property_RO
1055 def latlon(self):
1056 '''Get the I{geodetic} lat-, longitude in C{degrees} (L{LatLon2Tuple}C{(lat, lon)}).
1057 '''
1058 return LatLon2Tuple(self.lat, self.lon, name=self.name)
1060 @Property_RO
1061 def latlonheight(self):
1062 '''Get the I{geodetic} lat-, longitude in C{degrees} and height (L{LatLon3Tuple}C{(lat, lon, height)}).
1063 '''
1064 return self.latlon.to3Tuple(self.height)
1066 @Property_RO
1067 def north(self):
1068 '''Get the I{local} North, C{y} component (C{meter}).
1069 '''
1070 return self.y
1072 @Property_RO
1073 def phi(self):
1074 '''Get the I{geodetic} latitude in C{radians} (C{float}).
1075 '''
1076 return self.philam.phi
1078 @Property_RO
1079 def philam(self):
1080 '''Get the I{geodetic} lat-, longitude in C{radians} (L{PhiLam2Tuple}C{(phi, lam)}).
1081 '''
1082 return PhiLam2Tuple(radians(self.lat), radians(self.lon), name=self.name)
1084 @Property_RO
1085 def philamheight(self):
1086 '''Get the I{geodetic} lat-, longitude in C{radians} and height (L{PhiLam3Tuple}C{(phi, lam, height)}).
1087 '''
1088 return self.philam.to3Tuple(self.height)
1090 @Property_RO
1091 def slantrange(self):
1092 '''Get the I{local} slant Range, distance (C{meter}).
1093 '''
1094 return self.xyzLocal.aer4.slantrange
1096 def toAer(self, Aer=None, **Aer_kwds):
1097 '''Get the I{local} I{Azimuth, Elevation, slant Range} (AER) components.
1099 @kwarg Aer: Class to return AER (L{Aer}) or C{None}.
1100 @kwarg Aer_kwds: Optional, additional B{L{Aer}} keyword
1101 arguments, ignored if B{C{Aer}} is C{None}.
1103 @return: AER as an L{Aer} instance or if C{B{Aer} is None},
1104 an L{Aer4Tuple}C{(azimuth, elevation, slantrange, ltp)}.
1105 '''
1106 return self.xyzLocal.toAer(Aer=Aer, **Aer_kwds)
1108 def toCartesian(self, Cartesian=None, **Cartesian_kwds):
1109 '''Convert this I{local} to I{geocentric} C{(x, y, z)} (ECEF).
1111 @kwarg Cartesian: Optional class to return C{(x, y, z)} (C{Cartesian})
1112 or C{None}.
1113 @kwarg Cartesian_kwds: Optional, additional B{C{Cartesian}} keyword
1114 arguments, ignored if C{B{Cartesian} is None}.
1116 @return: A C{B{Cartesian}(x, y, z, **B{Cartesian_kwds})} instance
1117 or a L{Vector4Tuple}C{(x, y, z, h)} if C{B{Cartesian} is None}.
1119 @raise TypeError: Invalid B{C{Cartesian}} or B{C{Cartesian_kwds}}
1120 argument.
1121 '''
1122 return self.ecef.toCartesian(Cartesian=Cartesian, **Cartesian_kwds) # PYCHOK _Tuple
1124 def toEnu(self, Enu=None, **Enu_kwds):
1125 '''Get the I{local} I{East, North, Up} (ENU) components.
1127 @kwarg Enu: Class to return ENU (L{Enu}) or C{None}.
1128 @kwarg Enu_kwds: Optional, additional B{L{Enu}} keyword
1129 arguments, ignored if C{B{Enu} is None}.
1131 @return: ENU as an L{Enu} instance or if C{B{Enu} is None},
1132 an L{Enu4Tuple}C{(east, north, up, ltp)}.
1133 '''
1134 return self.xyzLocal.toEnu(Enu=Enu, **Enu_kwds)
1136 def toLatLon(self, LatLon=None, **LatLon_kwds):
1137 '''Convert this I{local} to I{geodetic} C{(lat, lon, height)}.
1139 @kwarg LatLon: Optional class to return C{(lat, lon, height)}
1140 (C{LatLon}) or C{None}.
1141 @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword
1142 arguments, ignored if C{B{LatLon} is None}.
1144 @return: An instance of C{B{LatLon}(lat, lon, **B{LatLon_kwds})}
1145 or if C{B{LatLon} is None}, a L{LatLon3Tuple}C{(lat, lon,
1146 height)} respectively L{LatLon4Tuple}C{(lat, lon, height,
1147 datum)} depending on whether C{datum} is un-/specified.
1149 @raise TypeError: Invalid B{C{LatLon}} or B{C{LatLon_kwds}}
1150 argument.
1151 '''
1152 return self.ecef.toLatLon(LatLon=LatLon, **LatLon_kwds) # PYCHOK _Tuple
1154 def toNed(self, Ned=None, **Ned_kwds):
1155 '''Get the I{local} I{North, East, Down} (NED) components.
1157 @kwarg Ned: Class to return NED (L{Ned}) or C{None}.
1158 @kwarg Ned_kwds: Optional, additional B{L{Ned}} keyword
1159 arguments, ignored if B{C{Ned}} is C{None}.
1161 @return: NED as an L{Ned} instance or if C{B{Ned} is None},
1162 an L{Ned4Tuple}C{(north, east, down, ltp)}.
1163 '''
1164 return self.xyzLocal.toNed(Ned=Ned, **Ned_kwds)
1166 def toXyz(self, Xyz=None, **Xyz_kwds):
1167 '''Get the I{local} I{X, Y, Z} (XYZ) components.
1169 @kwarg Xyz: Class to return XYZ (L{XyzLocal}) or C{None}.
1170 @kwarg Xyz_kwds: Optional, additional B{C{Xyz}} keyword
1171 arguments, ignored if C{B{Xyz} is None}.
1173 @return: XYZ as an B{C{Xyz}} instance or if C{B{Xyz} is None},
1174 an L{Xyz4Tuple}C{(x, y, z, ltp)}.
1175 '''
1176 return self.xyzLocal.toXyz(Xyz=Xyz, **Xyz_kwds)
1178 @Property_RO
1179 def up(self):
1180 '''Get the I{local} Up, C{z} component (C{meter}).
1181 '''
1182 return self.z
1184 @Property_RO
1185 def xyz(self):
1186 '''Get the I{local} C{(X, Y, Z)} components (L{Vector3Tuple}C{(x, y, z)}).
1187 '''
1188 return Vector3Tuple(self.x, self.y, self.z, name=self.name) # like Ecef9Tuple.xyz
1190 @Property_RO
1191 def xyzLocal(self):
1192 '''Get this L{Local9Tuple} as an L{XyzLocal}.
1193 '''
1194 return XyzLocal(*self.xyz, ltp=self.ltp, name=self.name) # PYCHOK .ltp
1197_XyzLocals4 = XyzLocal, Enu, Ned, Aer # PYCHOK in .ltp
1198_XyzLocals5 = _XyzLocals4 + (Local9Tuple,) # PYCHOK in .ltp
1201class ChLV9Tuple(Local9Tuple):
1202 '''9-Tuple C{(Y, X, h_, lat, lon, height, ltp, ecef, M)} with I{B{unfalsed} Swiss
1203 (Y, X, h_)} coordinates and height, all in C{meter}, C{ltp} either a L{ChLV},
1204 L{ChLVa} or L{ChLVe} instance and C{ecef} (L{EcefKarney} I{at Bern, Ch},
1205 otherwise like L{Local9Tuple}.
1206 '''
1207 _Names_ = (_Y_, _X_, _h__) + Local9Tuple._Names_[3:]
1209 @Property_RO
1210 def E_LV95(self):
1211 '''Get the B{falsed} I{Swiss E_LV95} easting (C{meter}).
1212 '''
1213 return self.EN2_LV95.E_LV95
1215 @Property_RO
1216 def EN2_LV95(self):
1217 '''Get the I{falsed Swiss (E_LV95, N_LV95)} easting and northing (L{ChLVEN2Tuple}).
1218 '''
1219 return ChLVEN2Tuple(*_MODS.ltp.ChLV.false2(self.Y, self.X, True), name=self.name)
1221 @Property_RO
1222 def h_LV03(self):
1223 '''Get the I{Swiss h_} height (C{meter}).
1224 '''
1225 return self.h_
1227 @Property_RO
1228 def h_LV95(self):
1229 '''Get the I{Swiss h_} height (C{meter}).
1230 '''
1231 return self.h_
1233 @property_RO
1234 def isChLV(self):
1235 '''Is this a L{ChLV}-generated L{ChLV9Tuple}?.
1236 '''
1237 return self.ltp.__class__ is _MODS.ltp.ChLV
1239 @property_RO
1240 def isChLVa(self):
1241 '''Is this a L{ChLVa}-generated L{ChLV9Tuple}?.
1242 '''
1243 return self.ltp.__class__ is _MODS.ltp.ChLVa
1245 @property_RO
1246 def isChLVe(self):
1247 '''Is this a L{ChLVe}-generated L{ChLV9Tuple}?.
1248 '''
1249 return self.ltp.__class__ is _MODS.ltp.ChLVe
1251 @Property_RO
1252 def N_LV95(self):
1253 '''Get the B{falsed} I{Swiss N_LV95} northing (C{meter}).
1254 '''
1255 return self.EN2_LV95.N_LV95
1257 @Property_RO
1258 def x(self):
1259 '''Get the I{local x, Swiss Y} easting (C{meter}).
1260 '''
1261 return self.Y
1263 @Property_RO
1264 def x_LV03(self):
1265 '''Get the B{falsed} I{Swiss x_LV03} northing (C{meter}).
1266 '''
1267 return self.yx2_LV03.x_LV03
1269 @Property_RO
1270 def y(self):
1271 '''Get the I{local y, Swiss X} northing (C{meter}).
1272 '''
1273 return self.X
1275 @Property_RO
1276 def y_LV03(self):
1277 '''Get the B{falsed} I{Swisss y_LV03} easting (C{meter}).
1278 '''
1279 return self.yx2_LV03.y_LV03
1281 @Property_RO
1282 def YX(self):
1283 '''Get the B{unfalsed} easting and northing (L{ChLVYX2Tuple}).
1284 '''
1285 return ChLVYX2Tuple(self.Y, self.X, name=self.name)
1287 @Property_RO
1288 def yx2_LV03(self):
1289 '''Get the B{falsed} I{Swiss (y_LV03, x_LV03)} easting and northing (L{ChLVyx2Tuple}).
1290 '''
1291 return ChLVyx2Tuple(*_MODS.ltp.ChLV.false2(self.Y, self.X, False), name=self.name)
1293 @Property_RO
1294 def z(self):
1295 '''Get the I{local z, Swiss h_} height (C{meter}).
1296 '''
1297 return self.h_
1300class ChLVYX2Tuple(_NamedTuple):
1301 '''2-Tuple C{(Y, X)} with B{unfalsed} I{Swiss LV95} easting and norting
1302 in C{meter}.
1303 '''
1304 _Names_ = (_Y_, _X_)
1305 _Units_ = ( Meter, Meter)
1307 def false2(self, LV95=True):
1308 '''Return the falsed C{Swiss LV95} or C{LV03} version of the projection.
1310 @see: Function L{ChLV.false2} for more information.
1311 '''
1312 return _MODS.ltp.ChLV.false2(*self, LV95=LV95, name=self.name)
1315class ChLVEN2Tuple(_NamedTuple):
1316 '''2-Tuple C{(E_LV95, N_LV95)} with B{falsed} I{Swiss LV95} easting and
1317 norting in C{meter (2_600_000, 1_200_000)} and origin at C{Bern, Ch}.
1318 '''
1319 _Names_ = ('E_LV95', 'N_LV95')
1320 _Units_ = ChLVYX2Tuple._Units_
1322 def unfalse2(self):
1323 '''Return this projection as an B{unfalsed} L{ChLVYX2Tuple}.
1325 @see: Function L{ChLV.unfalse2} for more information.
1326 '''
1327 return _MODS.ltp.ChLV.unfalse2(*self, LV95=True, name=self.name)
1330class ChLVyx2Tuple(_NamedTuple):
1331 '''2-Tuple C{(y_LV03, x_LV03)} with B{falsed} I{Swiss LV03} easting and
1332 norting in C{meter (600_000, 200_000)} and origin at C{Bern, Ch}.
1333 '''
1334 _Names_ = ('y_LV03', 'x_LV03')
1335 _Units_ = ChLVYX2Tuple._Units_
1337 def unfalse2(self):
1338 '''Return this projection as an B{unfalsed} L{ChLVYX2Tuple}.
1340 @see: Function L{ChLV.unfalse2} for more information.
1341 '''
1342 return _MODS.ltp.ChLV.unfalse2(*self, LV95=False, name=self.name)
1345class Footprint5Tuple(_NamedTuple):
1346 '''5-Tuple C{(center, upperleft, upperight, loweright, lowerleft)}
1347 with the C{center} and 4 corners of the I{local} projection of
1348 a C{Frustum}, each an L{Xyz4Tuple}, L{XyzLocal}, C{LatLon}, etc.
1350 @note: Misspelling of C{upperight} and C{loweright} is I{intentional}.
1351 '''
1352 _Names_ = (_center_, 'upperleft', 'upperight', 'loweright', 'lowerleft')
1353 _Units_ = (_Pass, _Pass, _Pass, _Pass, _Pass)
1355 def toLatLon5(self, ltp=None, LatLon=None, **LatLon_kwds):
1356 '''Convert this footprint's C{center} and 4 corners to I{geodetic}
1357 C{LatLon(lat, lon, height)}s or C{LatLon3-} or C{-4Tuple}s.
1359 @kwarg ltp: The I{local tangent plane} (L{Ltp}), overriding this
1360 footprint's C{center} or C{frustrum} C{ltp}.
1361 @kwarg LatLon: Optional I{geodetic} class (C{LatLon}) or C{None}.
1362 @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword
1363 arguments, ignored if C{B{LatLon} is None}.
1365 @return: A L{Footprint5Tuple} of 5 C{B{LatLon}(lat, lon,
1366 **B{LatLon_kwds})} instances or if C{B{LatLon} is None},
1367 5 L{LatLon3Tuple}C{(lat, lon, height)}s respectively
1368 5 L{LatLon4Tuple}C{(lat, lon, height, datum)}s depending
1369 on keyword argument C{datum} is un-/specified.
1371 @raise TypeError: Invalid B{C{ltp}}, B{C{LatLon}} or B{C{LatLon_kwds}}.
1373 @see: Methods L{XyzLocal.toLatLon} and L{Footprint5Tuple.xyzLocal5}.
1374 '''
1375 kwds = _xkwds(LatLon_kwds, ltp=_MODS.ltp._xLtp(ltp, self.center.ltp), # PYCHOK .center
1376 LatLon=LatLon, name=self.name,)
1377 return Footprint5Tuple(t.toLatLon(**kwds) for t in self.xyzLocal5())
1379 def xyzLocal5(self, ltp=None):
1380 '''Return this footprint's C{center} and 4 corners as 5 L{XyzLocal}s.
1382 @kwarg ltp: The I{local tangent plane} (L{Ltp}), overriding
1383 the {center} and corner C{ltp}s.
1385 @return: A L{Footprint5Tuple} of 5 L{XyzLocal} instances.
1387 @raise TypeError: Invalid B{C{ltp}}.
1388 '''
1389 if ltp is None:
1390 p = self
1391 else:
1392 p = _MODS.ltp._xLtp(ltp)
1393 p = tuple(Xyz4Tuple(t.x, t.y, t.z, p) for t in self)
1394 return Footprint5Tuple(t.xyzLocal for t in p)
1397__all__ += _ALL_DOCS(_NamedAerNed)
1399# **) MIT License
1400#
1401# Copyright (C) 2016-2023 -- mrJean1 at Gmail -- All Rights Reserved.
1402#
1403# Permission is hereby granted, free of charge, to any person obtaining a
1404# copy of this software and associated documentation files (the "Software"),
1405# to deal in the Software without restriction, including without limitation
1406# the rights to use, copy, modify, merge, publish, distribute, sublicense,
1407# and/or sell copies of the Software, and to permit persons to whom the
1408# Software is furnished to do so, subject to the following conditions:
1409#
1410# The above copyright notice and this permission notice shall be included
1411# in all copies or substantial portions of the Software.
1412#
1413# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1414# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1415# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1416# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
1417# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
1418# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
1419# OTHER DEALINGS IN THE SOFTWARE.