Coverage for pygeodesy/ltpTuples.py: 94%
493 statements
« prev ^ index » next coverage.py v7.2.2, created at 2023-04-12 11:45 -0400
« prev ^ index » next coverage.py v7.2.2, created at 2023-04-12 11:45 -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 issubclassof, _xinstanceof
15from pygeodesy.constants import _0_0, _90_0, _N_90_0
16from pygeodesy.dms import F_D, toDMS
17from pygeodesy.errors import _TypesError, _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_, _name_, _north_, \
22 _up_, _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__ = '22.10.11'
40_aer_ = 'aer'
41_alt_ = 'alt'
42_enu_ = 'enu'
43_h__ = 'h_'
44_ned_ = 'ned'
45_roll_ = 'roll'
46_slantrange_ = 'slantrange'
47_tilt_ = 'tilt'
48_yaw_ = 'yaw'
51def _er2gr(e, r):
52 '''(INTERNAL) Elevation and slant range to ground range.
53 '''
54 c = cos(radians(e))
55 return Meter_(groundrange=r * c)
58def _toStr2(inst, prec=None, fmt=Fmt.SQUARE, sep=_COMMASPACE_):
59 '''(INTERNAL) Get attribute name and value strings, joined and bracketed.
60 '''
61 a = inst._toStr # 'aer', 'enu', 'ned', 'xyz'
62 t = getattr(inst, a + _4_)[:len(a)]
63 t = strs(t, prec=3 if prec is None else prec)
64 if sep:
65 t = sep.join(t)
66 if fmt:
67 t = fmt(t)
68 return a, t
71def _4Tuple2Cls(inst, Cls, Cls_kwds):
72 '''(INTERNAL) Convert 4-Tuple to C{Cls} instance.
73 '''
74 if Cls is None:
75 return inst
76 elif issubclassof(Cls, Aer):
77 return inst.xyzLocal.toAer(Aer=Cls, **Cls_kwds)
78 elif issubclassof(Cls, Enu): # PYCHOK no cover
79 return inst.xyzLocal.toEnu(Enu=Cls, **Cls_kwds)
80 elif issubclassof(Cls, Ned):
81 return inst.xyzLocal.toNed(Ned=Cls, **Cls_kwds)
82 elif issubclassof(Cls, XyzLocal): # PYCHOK no cover
83 return inst.xyzLocal.toXyz(Xyz=Cls, **Cls_kwds)
84 elif Cls is Local9Tuple: # PYCHOK no cover
85 return inst.xyzLocal.toLocal9Tuple(**Cls_kwds)
86 n = inst.__class__.__name__[:3] # PYCHOK no cover
87 raise _TypesError(n, Cls, Aer, Enu, Ned, XyzLocal)
90def _xyz2aer4(inst):
91 '''(INTERNAL) Convert C{(x, y, z}) to C{(A, E, R)}.
92 '''
93 x, y, z, _ = inst.xyz4
94 A = Bearing(azimuth=atan2b(x, y))
95 E = Degrees(elevation=atan2d(z, hypot(x, y)))
96 R = Meter(slantrange=hypot_(x, y, z))
97 return Aer4Tuple(A, E, R, inst.ltp, name=inst.name)
100class _NamedAerNed(_NamedBase):
101 '''(INTERNAL) Base class for classes C{Aer} and C{Ned}.
102 '''
103 _ltp = None # local tangent plane (C{Ltp}), origin
105 @Property_RO
106 def ltp(self):
107 '''Get the I{local tangent plane} (L{Ltp}).
108 '''
109 return self._ltp
111 def toAer(self, Aer=None, **Aer_kwds):
112 '''Get the I{local} I{Azimuth, Elevation, slant Range} (AER) components.
114 @kwarg Aer: Class to return AER (L{Aer}) or C{None}.
115 @kwarg Aer_kwds: Optional, additional B{L{Aer}} keyword
116 arguments, ignored if B{C{Aer}} is C{None}.
118 @return: AER as an L{Aer} instance or if C{B{Aer} is None},
119 an L{Aer4Tuple}C{(azimuth, elevation, slantrange, ltp)}.
120 '''
121 return self.xyz4._toXyz(Aer, Aer_kwds)
123 def toEnu(self, Enu=None, **Enu_kwds):
124 '''Get the I{local} I{East, North, Up} (ENU) components.
126 @kwarg Enu: Class to return ENU (L{Enu}) or C{None}.
127 @kwarg Enu_kwds: Optional, additional B{L{Enu}} keyword
128 arguments, ignored if C{B{Enu} is None}.
130 @return: ENU as an L{Enu} instance or if C{B{Enu} is None},
131 an L{Enu4Tuple}C{(east, north, up, ltp)}.
132 '''
133 return self.xyz4._toXyz(Enu, Enu_kwds)
135 def toNed(self, Ned=None, **Ned_kwds):
136 '''Get the I{local} I{North, East, Down} (NED) components.
138 @kwarg Ned: Class to return NED (L{Ned}) or C{None}.
139 @kwarg Ned_kwds: Optional, additional B{L{Ned}} keyword
140 arguments, ignored if B{C{Ned}} is C{None}.
142 @return: NED as an L{Ned} instance or if C{B{Ned} is None},
143 an L{Ned4Tuple}C{(north, east, down, ltp)}.
144 '''
145 return self.xyz4._toXyz(Ned, Ned_kwds)
147 def toXyz(self, Xyz=None, **Xyz_kwds):
148 '''Get the local I{X, Y, Z} (XYZ) components.
150 @kwarg Xyz: Class to return XYZ (L{XyzLocal}, L{Enu},
151 L{Ned}, L{Aer}) or C{None}.
152 @kwarg Xyz_kwds: Optional, additional B{C{Xyz}} keyword
153 arguments, ignored if C{B{Xyz} is None}.
155 @return: XYZ as an B{C{Xyz}} instance or if C{B{Xyz} is None},
156 an L{Xyz4Tuple}C{(x, y, z, ltp)}.
158 @raise TypeError: Invalid B{C{Xyz}}.
159 '''
160 return self.xyz4._toXyz(Xyz, Xyz_kwds)
162 @Property_RO
163 def xyz(self):
164 '''Get the I{local} C{(X, Y, Z)} coordinates (L{Vector3Tuple}C{(x, y, z)}).
165 '''
166 return Vector3Tuple(self.x, self.y, self.z, name=self.name) # like Ecef9Tuple.xyz, Local6tuple.xyz
168 @property_RO
169 def xyz4(self):
170 '''(INTERNAL) I{Must be overloaded}, see function C{notOverloaded}.
171 '''
172 notOverloaded(self)
174 @Property_RO
175 def xyzLocal(self):
176 '''Get this AER or NED as an L{XyzLocal}.
177 '''
178 return XyzLocal(self.xyz4, name=self.name)
181class Aer(_NamedAerNed):
182 '''Local C{Azimuth-Elevation-Range} (AER) in a I{local tangent plane}.
183 '''
184 _azimuth = _0_0 # bearing from North (C{degrees360})
185 _elevation = _0_0 # tilt, pitch from horizon (C{degrees}).
186# _ltp = None # local tangent plane (C{Ltp}), origin
187 _slantrange = _0_0 # distance (C{Meter})
188 _toStr = _aer_
190 def __init__(self, aer, elevation=_0_0, slantrange=_0_0, ltp=None, name=NN):
191 '''New L{Aer}.
193 @arg aer: Scalar azimuth, bearing from North (C{degrees360}) or
194 local (L{Aer}, L{Aer4Tuple}).
195 @kwarg elevation: Scalar angle I{above} horizon, I{above} B{C{ltp}}
196 (C{degrees90}) if scalar B{C{aer}}.
197 @kwarg slantrange: Scalar distance (C{meter}) if scalar B{C{aer}}.
198 @kwarg ltp: The I{local tangent plane}, (geodetic) origin (L{Ltp},
199 L{LocalCartesian}).
200 @kwarg name: Optional name (C{str}).
202 @raise TypeError: Invalid B{C{ltp}}.
204 @raise UnitError: Invalid B{C{azimuth}}, B{C{elevation}} or
205 or B{C{slantrange}}.
206 '''
207 try: # PYCHOK no cover
208 self._azimuth, self._elevation, self._slantrange = \
209 aer.azimuth, aer.elevation, aer.slantrange
210 _xinstanceof(Aer, Aer4Tuple, aer=aer)
211 p = getattr(aer, _ltp_, ltp)
212 n = getattr(aer, _name_, name)
213 except AttributeError:
214 self._azimuth = Bearing(azimuth=aer)
215 self._elevation = Degrees_(elevation=elevation, low=_N_90_0, high=_90_0)
216 self._slantrange = Meter_(slantrange=slantrange)
217 p, n = ltp, name
219 if p:
220 self._ltp = _MODS.ltp._xLtp(p)
221 if name:
222 self.name = n
224 @Property_RO
225 def aer4(self):
226 '''Get the C{(azimuth, elevation, slantrange, ltp)} components (L{Aer4Tuple}).
227 '''
228 return Aer4Tuple(self._azimuth, self._elevation, self._slantrange, self.ltp, name=self.name)
230 @Property_RO
231 def azimuth(self):
232 '''Get the Azimuth, bearing from North (C{degrees360}).
233 '''
234 return self._azimuth
236 @Property_RO
237 def down(self):
238 '''Get the Down component (C{meter}).
239 '''
240 return self.xyzLocal.down
242 @Property_RO
243 def east(self):
244 '''Get the East component (C{meter}).
245 '''
246 return self.xyzLocal.east
248 @Property_RO
249 def elevation(self):
250 '''Get the Elevation, tilt above horizon (C{degrees90}).
251 '''
252 return self._elevation
254 @Property_RO
255 def groundrange(self):
256 '''Get the I{ground range}, distance (C{meter}).
257 '''
258 return _er2gr(self._elevation, self._slantrange)
260 @Property_RO
261 def north(self):
262 '''Get the North component (C{meter}).
263 '''
264 return self.xyzLocal.north
266 @Property_RO
267 def slantrange(self):
268 '''Get the I{slant Range}, distance (C{meter}).
269 '''
270 return self._slantrange
272 def toRepr(self, prec=None, fmt=Fmt.SQUARE, sep=_COMMASPACE_, **unused): # PYCHOK expected
273 '''Return a string representation of this AER as azimuth
274 (bearing), elevation and slant range.
276 @kwarg prec: Number of (decimal) digits, unstripped (C{int}).
277 @kwarg fmt: Enclosing backets format (C{str}).
278 @kwarg sep: Optional separator between AERs (C{str}).
280 @return: This AER as "[A:degrees360, E:degrees90, R:meter]" (C{str}).
281 '''
282 t = (toDMS(self.azimuth, form=F_D, prec=prec, ddd=0),
283 toDMS(self.elevation, form=F_D, prec=prec, ddd=0),
284 fstr( self.slantrange, prec=3 if prec is None else prec))
285 return _xzipairs(self._toStr.upper(), t, sep=sep, fmt=fmt)
287 def toStr(self, **prec_fmt_sep): # PYCHOK expected
288 '''Return a string representation of this AER.
290 @kwarg prec_fmt_sep: Keyword arguments C{B{prec}=3} for the
291 number of (decimal) digits, unstripped
292 (C{int}), C{B{fmt}='[]'} the enclosing
293 backets format (C{str}) and separator
294 C{B{sep}=', '} to join (C{str}).
296 @return: This AER as "[degrees360, degrees90, meter]" (C{str}).
297 '''
298 _, t = _toStr2(self, **prec_fmt_sep)
299 return t
301 @Property_RO
302 def up(self):
303 '''Get the Up component (C{meter}).
304 '''
305 return self.xyzLocal.up
307 @Property_RO
308 def x(self):
309 '''Get the X component (C{meter}).
310 '''
311 return self.xyz4.x
313 @Property_RO
314 def xyz4(self):
315 '''Get the C{(x, y, z, ltp)} components (L{Xyz4Tuple}).
316 '''
317 sA, cA, sE, cE = sincos2d_(self._azimuth, self._elevation)
318 R = self._slantrange
319 r = cE * R # ground range
320 return Xyz4Tuple(sA * r, cA * r, sE * R, self.ltp, name=self.name)
322 @Property_RO
323 def y(self):
324 '''Get the Y component (C{meter}).
325 '''
326 return self.xyz4.y
328 @Property_RO
329 def z(self):
330 '''Get the Z component (C{meter}).
331 '''
332 return self.xyz4.z
335class Aer4Tuple(_NamedTuple):
336 '''4-Tuple C{(azimuth, elevation, slantrange, ltp)},
337 all in C{meter} except C{ltp}.
338 '''
339 _Names_ = (_azimuth_, _elevation_, _slantrange_, _ltp_)
340 _Units_ = ( Meter, Meter, Meter, _Pass)
342 def _toAer(self, Cls, Cls_kwds):
343 '''(INTERNAL) Return C{Cls(..., **Cls_kwds)} instance.
344 '''
345 if issubclassof(Cls, Aer):
346 return Cls(*self, **_xkwds(Cls_kwds, name=self.name))
347 else:
348 return _4Tuple2Cls(self, Cls, Cls_kwds)
350 @Property_RO
351 def groundrange(self):
352 '''Get the I{ground range}, distance (C{meter}).
353 '''
354 return _er2gr(self.elevation, self.slantrange) # PYCHOK _Tuple
356 @Property_RO
357 def xyzLocal(self):
358 '''Get this L{Aer4Tuple} as an L{XyzLocal}.
359 '''
360 return Aer(self).xyzLocal
363class Attitude4Tuple(_NamedTuple):
364 '''4-Tuple C{(alt, tilt, yaw, roll)} with C{altitude} in (positive)
365 C{meter} and C{tilt}, C{yaw} and C{roll} in C{degrees} representing
366 the attitude of a plane or camera.
367 '''
368 _Names_ = (_alt_, _tilt_, _yaw_, _roll_)
369 _Units_ = ( Meter, Bearing, Degrees, Degrees)
371 @Property_RO
372 def atyr(self):
373 '''Return this attitude (L{Attitude4Tuple}).
374 '''
375 return self
377 @Property_RO
378 def tyr3d(self):
379 '''Get this attitude's (3-D) directional vector (L{Vector3d}).
380 '''
381 return _MODS.ltp.Attitude(self).tyr3d
384class Ned(_NamedAerNed):
385 '''Local C{North-Eeast-Down} (NED) location in a I{local tangent plane}.
387 @see: L{Enu} and L{Ltp}.
388 '''
389 _down = _0_0 # down, -XyzLocal.z (C{meter}).
390 _east = _0_0 # east, XyzLocal.y (C{meter}).
391# _ltp = None # local tangent plane (C{Ltp}), origin
392 _north = _0_0 # north, XyzLocal.x (C{meter})
393 _toStr = _ned_
395 def __init__(self, ned, east=_0_0, down=_0_0, ltp=None, name=NN):
396 '''New L{Ned} vector.
398 @arg ned: Scalar north component (C{meter}) or local NED (L{Ned},
399 L{Ned4Tuple}).
400 @kwarg east: Scalar east component (C{meter}) if scalar B{C{ned}}.
401 @kwarg down: Scalar down component, normal to I{inside} surface of
402 the ellipsoid or sphere (C{meter}) if scalar B{C{ned}}.
403 @kwarg ltp: The I{local tangent plane}, (geodetic) origin (L{Ltp},
404 L{LocalCartesian}).
405 @kwarg name: Optional name (C{str}).
407 @raise TypeError: Invalid B{C{ltp}}.
409 @raise UnitError: Invalid B{C{north}}, B{C{east}} or B{C{down}}.
410 '''
411 try: # PYCHOK no cover
412 self._north, self._east, self._down = ned.north, ned.east, ned.down
413 _xinstanceof(Ned, Ned4Tuple, ned=ned)
414 p = getattr(ned, _ltp_, ltp)
415 n = getattr(ned, _name_, name)
416 except AttributeError:
417 self._north = Meter(north=ned or _0_0)
418 self._east = Meter(east=east or _0_0)
419 self._down = Meter(down=down or _0_0)
420 p, n = ltp, name
422 if p:
423 self._ltp = _MODS.ltp._xLtp(p)
424 if n:
425 self.name = n
427 @Property_RO
428 def aer4(self):
429 '''Get the C{(azimuth, elevation, slantrange, ltp)} components (L{Aer4Tuple}).
430 '''
431 return _xyz2aer4(self)
433 @Property_RO
434 def azimuth(self):
435 '''Get the Azimuth, bearing from North (C{degrees360}).
436 '''
437 return self.aer4.azimuth
439 @deprecated_Property_RO
440 def bearing(self):
441 '''DEPRECATED, use C{azimuth}.'''
442 return self.azimuth
444 @Property_RO
445 def down(self):
446 '''Get the Down component (C{meter}).
447 '''
448 return self._down
450 @Property_RO
451 def east(self):
452 '''Get the East component (C{meter}).
453 '''
454 return self._east
456 @Property_RO
457 def elevation(self):
458 '''Get the Elevation, tilt above horizon (C{degrees90}).
459 '''
460 return self.aer4.elevation # neg(degrees90(asin1(self.down / self.length))))
462 @Property_RO
463 def groundrange(self):
464 '''Get the I{ground range}, distance (C{meter}).
465 '''
466 return Meter(groundrange=hypot(self.north, self.east))
468 @deprecated_Property_RO
469 def length(self):
470 '''DEPRECATED, use C{slantrange}.'''
471 return self.slantrange
473 @deprecated_Property_RO
474 def ned(self):
475 '''DEPRECATED, use property C{ned4}.'''
476 return _MODS.deprecated.Ned3Tuple(self.north, self.east, self.down, name=self.name)
478 @Property_RO
479 def ned4(self):
480 '''Get the C{(north, east, down, ltp)} components (L{Ned4Tuple}).
481 '''
482 return Ned4Tuple(self.north, self.east, self.down, self.ltp, name=self.name)
484 @Property_RO
485 def north(self):
486 '''Get the North component (C{meter}).
487 '''
488 return self._north
490 @Property_RO
491 def slantrange(self):
492 '''Get the I{slant Range}, distance (C{meter}).
493 '''
494 return self.aer4.slantrange
496 @deprecated_method
497 def to3ned(self): # PYCHOK no cover
498 '''DEPRECATED, use property L{ned4}.'''
499 return self.ned # XXX deprecated too
501 def toRepr(self, prec=None, fmt=Fmt.SQUARE, sep=_COMMASPACE_, **unused): # PYCHOK expected
502 '''Return a string representation of this NED.
504 @kwarg prec: Number of (decimal) digits, unstripped (C{int}).
505 @kwarg fmt: Enclosing backets format (C{str}).
506 @kwarg sep: Separator to join (C{str}).
508 @return: This NED as "[N:meter, E:meter, D:meter]" (C{str}).
509 '''
510 a, t = _toStr2(self, prec=prec, fmt=NN, sep=NN)
511 return _xzipairs(a.upper(), t, sep=sep, fmt=fmt)
513 def toStr(self, **prec_fmt_sep): # PYCHOK expected
514 '''Return a string representation of this NED.
516 @kwarg prec_fmt_sep: Keyword arguments C{B{prec}=3} for the
517 number of (decimal) digits, unstripped
518 (C{int}), C{B{fmt}='[]'} the enclosing
519 backets format (C{str}) and separator
520 C{B{sep}=', '} to join (C{str}).
522 @return: This NED as "[meter, meter, meter]" (C{str}).
523 '''
524 _, t = _toStr2(self, **prec_fmt_sep)
525 return t
527 @deprecated_method
528 def toVector3d(self):
529 '''DEPRECATED, use property L{xyz}.'''
530 return self.xyz
532 @Property_RO
533 def up(self):
534 '''Get the Up component (C{meter}).
535 '''
536 return Meter(up=-self._down) # negated
538 @Property_RO
539 def x(self):
540 '''Get the X component (C{meter}).
541 '''
542 return Meter(x=self._east) # 2nd arg, E
544 @Property_RO
545 def xyz4(self):
546 '''Get the C{(x, y, z, ltp)} components (L{Xyz4Tuple}).
547 '''
548 return Xyz4Tuple(self.x, self.y, self.z, self.ltp, name=self.name)
550 @Property_RO
551 def y(self):
552 '''Get the Y component (C{meter}).
553 '''
554 return Meter(y=self._north) # 1st arg N
556 @Property_RO
557 def z(self):
558 '''Get the Z component (C{meter}).
559 '''
560 return Meter(z=-self._down) # negated
563class Ned4Tuple(_NamedTuple):
564 '''4-Tuple C{(north, east, down, ltp)}, all in C{meter} except C{ltp}.
565 '''
566 _Names_ = (_north_, _east_, _down_, _ltp_)
567 _Units_ = ( Meter, Meter, Meter, _Pass)
569 def _toNed(self, Cls, Cls_kwds):
570 '''(INTERNAL) Return C{Cls(..., **Cls_kwds)} instance.
571 '''
572 if issubclassof(Cls, Ned):
573 return Cls(*self, **_xkwds(Cls_kwds, name=self.name))
574 else:
575 return _4Tuple2Cls(self, Cls, Cls_kwds)
577 @Property_RO
578 def xyzLocal(self):
579 '''Get this L{Ned4Tuple} as an L{XyzLocal}.
580 '''
581 return Ned(self).xyzLocal
584class XyzLocal(Vector3d):
585 '''Local C{(x, y, z)} in a I{local tangent plane} (LTP)
586 and base class for local L{Enu}, L{Ned} and L{Aer}.
587 '''
588 _ltp = None # local tangent plane (C{Ltp}), origin
589 _toStr = _xyz_
591 def __init__(self, x_xyz, y=_0_0, z=_0_0, ltp=None, name=NN):
592 '''New L{XyzLocal}.
594 @arg x_xyz: Scalar X component (C{meter}), C{positive east} or
595 a local (L{XyzLocal}, L{Xyz4Tuple}, L{Enu},
596 L{Enu4Tuple}, L{Local9Tuple}).
597 @kwarg y: Scalar Y component (C{meter}) for scalar B{C{x_xyz}},
598 C{positive north}.
599 @kwarg z: Scalar Z component for scalar B{C{x_xyz}}, normal
600 C{positive up} from the surface of the ellipsoid
601 or sphere (C{meter}).
602 @kwarg ltp: The I{local tangent plane}, (geodetic) origin (L{Ltp},
603 L{LocalCartesian}).
605 @raise TypeError: Invalid B{C{x_xyz}} or B{C{ltp}}.
607 @raise UnitError: Invalid scalar B{C{x_xyz}}, B{C{y}} or B{C{z}}.
608 '''
609 try:
610 self._x, self._y, self._z = x_xyz.x, x_xyz.y, x_xyz.z
611 _xinstanceof(XyzLocal, Xyz4Tuple, Enu, Enu4Tuple, Local9Tuple, Ned,
612 **{self._toStr: x_xyz})
613 p = getattr(x_xyz, _ltp_, ltp)
614 n = name or getattr(x_xyz, _name_, NN)
615 except AttributeError:
616 self._x = Meter(x=x_xyz or _0_0)
617 self._y = Meter(y=y or _0_0)
618 self._z = Meter(z=z or _0_0)
619 p, n = ltp, name
621 if p:
622 self._ltp = _MODS.ltp._xLtp(p)
623 if n:
624 self.name = n
626 def __str__(self):
627 return self.toStr()
629 @Property_RO
630 def aer4(self):
631 '''Get the C{(azimuth, elevation, slantrange, ltp)} components (L{Aer4Tuple}).
632 '''
633 return _xyz2aer4(self)
635 @Property_RO
636 def azimuth(self):
637 '''Get the Azimuth, bearing from North (C{degrees360}).
639 @see: U{Azimuth<https://GSSC.ESA.int/navipedia/index.php/
640 Transformations_between_ECEF_and_ENU_coordinates>}.
641 '''
642 return self.aer4.azimuth
644 def classof(self, *args, **kwds): # PYCHOK no cover
645 '''Create another instance of this very class.
647 @arg args: Optional, positional arguments.
648 @kwarg kwds: Optional, keyword arguments.
650 @return: New instance (C{self.__class__}).
651 '''
652 kwds = _xkwds(kwds, ltp=self.ltp, name=self.name)
653 return self.__class__(*args, **kwds)
655 @Property_RO
656 def down(self):
657 '''Get the Down component (C{meter}).
658 '''
659 return Meter(down=-self.z)
661 @property_RO
662 def ecef(self):
663 '''Get this LTP's ECEF converter (C{Ecef...} I{instance}).
664 '''
665 return self.ltp.ecef
667 @Property_RO
668 def east(self):
669 '''Get the East component (C{meter}).
670 '''
671 return Meter(east=self.x)
673 @Property_RO
674 def elevation(self):
675 '''Get the Elevation, tilt above horizon (C{degrees90}).
677 @see: U{Elevation<https://GSSC.ESA.int/navipedia/index.php/
678 Transformations_between_ECEF_and_ENU_coordinates>}.
679 '''
680 return self.aer4.elevation # neg(degrees90(asin1(self.down / self.length))))
682 @Property_RO
683 def enu4(self):
684 '''Get the C{(east, north, up, ltp)} components (L{Enu4Tuple}).
685 '''
686 return Enu4Tuple(self.east, self.north, self.up, self.ltp, name=self.name)
688 @Property_RO
689 def groundrange(self):
690 '''Get the I{ground range}, distance (C{meter}).
691 '''
692 return Meter(groundrange=hypot(self.x, self.y))
694 @Property_RO
695 def ltp(self):
696 '''Get the I{local tangent plane} (L{Ltp}).
697 '''
698 return self._ltp
700 @Property_RO
701 def ned4(self):
702 '''Get the C{(north, east, down, ltp)} components (L{Ned4Tuple}).
703 '''
704 return Ned4Tuple(self.north, self.east, self.down, self.ltp, name=self.name)
706 @Property_RO
707 def north(self):
708 '''Get the North component (C{meter}).
709 '''
710 return Meter(north=self.y)
712 @Property_RO
713 def slantrange(self):
714 '''Get the I{slant Range}, distance (C{meter}).
715 '''
716 return self.aer4.slantrange
718 def toAer(self, Aer=None, **Aer_kwds):
719 '''Get the local I{Azimuth, Elevation, slantRange} components.
721 @kwarg Aer: Class to return AER (L{Aer}) or C{None}.
722 @kwarg Aer_kwds: Optional, additional B{C{Aer}} keyword
723 arguments, ignored if C{B{Aer} is None}.
725 @return: AER as an L{Aer} instance or if C{B{Aer} is None}, an
726 L{Aer4Tuple}C{(azimuth, elevation, slantrange, ltp)}.
728 @raise TypeError: Invalid B{C{Aer}}.
729 '''
730 return self.aer4._toAer(Aer, Aer_kwds)
732 def toCartesian(self, Cartesian=None, ltp=None, **Cartesian_kwds):
733 '''Get the geocentric C{(x, y, z)} (ECEF) coordinates of this local.
735 @kwarg Cartesian: Optional class to return C{(x, y, z)} (C{Cartesian})
736 or C{None}.
737 @kwarg ltp: Optional I{local tangent plane} (LTP) (L{Ltp}),
738 overriding this C{ltp}.
739 @kwarg Cartesian_kwds: Optional, additional B{C{Cartesian}} keyword
740 arguments, ignored if C{B{Cartesian} is None}.
742 @return: A B{C{Cartesian}} instance of if C{B{Cartesian} is None}, an
743 L{Ecef9Tuple}C{(x, y, z, lat, lon, height, C, M, datum)}
744 with C{M=None}, always.
746 @raise TypeError: Invalid B{C{ltp}}, B{C{Cartesian}} or
747 B{C{Cartesian_kwds}} argument.
748 '''
749 ltp = _MODS.ltp._xLtp(ltp, self.ltp)
750 if Cartesian is None:
751 r = ltp._local2ecef(self, nine=True)
752 else:
753 x, y, z = ltp._local2ecef(self)
754 kwds = _xkwds(Cartesian_kwds, datum=ltp.datum)
755 r = Cartesian(x, y, z, **kwds)
756 return _xnamed(r, self.name or ltp.name)
758 def toEnu(self, Enu=None, **Enu_kwds):
759 '''Get the local I{East, North, Up} (ENU) components.
761 @kwarg Enu: Class to return ENU (L{Enu}) or C{None}.
762 @kwarg Enu_kwds: Optional, additional B{C{Enu}} keyword
763 arguments, ignored if C{B{Enu} is None}.
765 @return: ENU as an L{Enu} instance or if C{B{Enu} is None},
766 an L{Enu4Tuple}C{(east, north, up, ltp)}.
767 '''
768 return self.enu4._toEnu(Enu, Enu_kwds)
770 def toLatLon(self, LatLon=None, ltp=None, **LatLon_kwds):
771 '''Get the geodetic C{(lat, lon, height)} coordinates if this local.
773 @kwarg LatLon: Optional class to return C{(x, y, z)} (C{LatLon})
774 or C{None}.
775 @kwarg ltp: Optional I{local tangent plane} (LTP) (L{Ltp}),
776 overriding this ENU/NED/AER/XYZ's LTP.
777 @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword
778 arguments, ignored if C{B{LatLon} is None}.
780 @return: An B{C{LatLon}} instance of if C{B{LatLon} is None}, an
781 L{Ecef9Tuple}C{(x, y, z, lat, lon, height, C, M,
782 datum)} with C{M=None}, always.
784 @raise TypeError: Invalid B{C{ltp}}, B{C{LatLon}} or
785 B{C{LatLon_kwds}} argument.
786 '''
787 ltp = _MODS.ltp._xLtp(ltp, self.ltp)
788 r = ltp._local2ecef(self, nine=True)
789 if LatLon is None:
790 r = _xnamed(r, self.name or ltp.name)
791 else:
792 kwds = _xkwds(LatLon_kwds, height=r.height, datum=r.datum,
793 name=self.name or ltp.name)
794 r = LatLon(r.lat, r.lon, **kwds) # XXX ltp?
795 return r
797 def toLocal9Tuple(self, M=False, name=NN):
798 '''Get this local as a C{Local9Tuple}.
800 @kwarg M: Optionally include the rotation matrix (C{bool}).
801 @kwarg name: Optional name (C{str}).
803 @return: L{Local9Tuple}C{(x, y, z, lat, lon, height, ltp,
804 ecef, M)} with C{ltp} this C{Ltp}, C{ecef} an
805 L{Ecef9Tuple} and C{M} L{EcefMatrix} or C{None}.
806 '''
807 ltp = self.ltp # see C{self.toLatLon}
808 t = ltp._local2ecef(self, nine=True, M=M)
809 return Local9Tuple(self.x, self.y, self.z, t.lat, t.lon, t.height,
810 ltp, t, t.M, name=name or t.name)
812 def toNed(self, Ned=None, **Ned_kwds):
813 '''Get the local I{North, East, Down} (Ned) components.
815 @kwarg Ned: Class to return NED (L{Ned}) or C{None}.
816 @kwarg Ned_kwds: Optional, additional B{C{Ned}} keyword
817 arguments, ignored if C{B{Ned} is None}.
819 @return: NED as an L{Ned} instance or if C{B{Ned} is None},
820 an L{Ned4Tuple}C{(north, east, down, ltp)}.
821 '''
822 return self.ned4._toNed(Ned, Ned_kwds)
824 def toRepr(self, prec=None, fmt=Fmt.SQUARE, sep=_COMMASPACE_, **unused): # PYCHOK expected
825 '''Return a string representation of this ENU/NED/XYZ.
827 @kwarg prec: Number of (decimal) digits, unstripped (C{int}).
828 @kwarg fmt: Enclosing backets format (C{str}).
829 @kwarg sep: Separator to join (C{str}).
831 @return: This XYZ/ENU as "[E:meter, N:meter, U:meter]",
832 "[N:meter, E:meter, D:meter]" respectively
833 "[X:meter, Y:meter, Z:meter]" (C{str}).
834 '''
835 a, t = _toStr2(self, prec=prec, fmt=NN, sep=NN)
836 return _xzipairs(a.upper(), t, sep=sep, fmt=fmt)
838 def toStr(self, **prec_fmt_sep): # PYCHOK expected
839 '''Return a string representation of this XYZ.
841 @kwarg prec_fmt_sep: Keyword arguments C{B{prec}=3} for the
842 number of (decimal) digits, unstripped
843 (C{int}), C{B{fmt}='[]'} the enclosing
844 backets format (C{str}) and separator
845 C{B{sep}=', '} to join (C{str}).
847 @return: This XYZ as "[meter, meter, meter]" (C{str}).
848 '''
849 _, t = _toStr2(self, **prec_fmt_sep)
850 return t
852 def toXyz(self, Xyz=None, **Xyz_kwds):
853 '''Get the local I{X, Y, Z} (XYZ) components.
855 @kwarg Xyz: Class to return XYZ (L{XyzLocal}, L{Enu},
856 L{Ned}, L{Aer}) or C{None}.
857 @kwarg Xyz_kwds: Optional, additional B{C{Xyz}} keyword
858 arguments, ignored if C{B{Xyz} is None}.
860 @return: XYZ as an B{C{Xyz}} instance or if C{B{Xyz} is None},
861 an L{Xyz4Tuple}C{(x, y, z, ltp)}.
862 '''
863 return self.xyz4._toXyz(Xyz, Xyz_kwds)
865 @Property_RO
866 def up(self):
867 '''Get the Up component (C{meter}).
868 '''
869 return Meter(up=self.z)
871# @Property_RO
872# def x(self): # see: Vector3d.x
873# '''Get the X component (C{meter}).
874# '''
875# return self._x
877# @Property_RO
878# def xyz(self): # see: Vector3d.xyz
879# '''Get the I{local} C{(X, Y, Z)} coordinates (L{Vector3Tuple}C{(x, y, z)}).
880# '''
881# return Vector3Tuple(self.x, self.y, self.z, name=self.name) # like Ecef9Tuple.xyz, Local6tuple.xyz
883 @Property_RO
884 def xyz4(self):
885 '''Get the C{(x, y, z, ltp)} components (L{Xyz4Tuple}).
886 '''
887 return Xyz4Tuple(self.x, self.y, self.z, self.ltp, name=self.name)
889 @Property_RO
890 def xyzLocal(self):
891 '''Get this L{XyzLocal}.
892 '''
893 return self
895# @Property_RO
896# def y(self): # see: Vector3d.y
897# '''Get the Y component (C{meter}).
898# '''
899# return self._y
901# @Property_RO
902# def z(self): # see: Vector3d.z
903# '''Get the Z component (C{meter}).
904# '''
905# return self._z
908class Xyz4Tuple(_NamedTuple):
909 '''4-Tuple C{(x, y, z, ltp)}, all in C{meter} except C{ltp}.
910 '''
911 _Names_ = (_x_, _y_, _z_, _ltp_)
912 _Units_ = ( Meter, Meter, Meter, _Pass)
914 def _toXyz(self, Cls, Cls_kwds):
915 '''(INTERNAL) Return C{Cls(..., **Cls_kwds)} instance.
916 '''
917 if issubclassof(Cls, XyzLocal):
918 return Cls(*self, **_xkwds(Cls_kwds, name=self.name))
919 else:
920 return _4Tuple2Cls(self, Cls, Cls_kwds)
922 @Property_RO
923 def xyzLocal(self):
924 '''Get this L{Xyz4Tuple} as an L{XyzLocal}.
925 '''
926 return XyzLocal(self, ltp=self.ltp, name=self.name) # PYCHOK .ltp
929class Enu(XyzLocal):
930 '''Local C{Eeast-North-Up} (ENU) location in a I{local tangent plane}.
932 @see: U{East, North, Up (ENU)<https://GSSC.ESA.int/navipedia/index.php/
933 Transformations_between_ECEF_and_ENU_coordinates>} coordinates.
934 '''
935 _toStr = _enu_
937 def __init__(self, enu, north=_0_0, up=_0_0, ltp=None, name=NN):
938 '''New L{Enu}.
940 @arg enu: Scalar East component (C{meter}) or a local (L{Enu},
941 (L{XyzLocal}, L{Local9Tuple}).
942 @kwarg north: Scalar North component (C{meter}) if scalar B{C{enu}}.
943 @kwarg up: Scalar Up component if scalar B{C{enu}}, normal from the
944 surface of the ellipsoid or sphere (C{meter}).
945 @kwarg ltp: The I{local tangent plane}, (geodetic) origin (L{Ltp},
946 L{LocalCartesian}).
947 @kwarg name: Optional name (C{str}).
949 @raise TypeError: Invalid B{C{enu}} or B{C{ltp}}.
951 @raise UnitError: Invalid B{C{east}}, B{C{north}} or B{C{up}}.
952 '''
953 XyzLocal.__init__(self, enu, north, up, ltp=ltp, name=name)
955# @Property_RO
956# def xyzLocal(self):
957# return XyzLocal(self, name=self.name)
960class Enu4Tuple(_NamedTuple):
961 '''4-Tuple C{(east, north, up, ltp)}, in C{meter} except C{ltp}.
962 '''
963 _Names_ = (_east_, _north_, _up_, _ltp_)
964 _Units_ = ( Meter, Meter, Meter, _Pass)
966 def _toEnu(self, Cls, Cls_kwds):
967 '''(INTERNAL) Return C{Cls(..., **Cls_kwds)} instance.
968 '''
969 if issubclassof(Cls, XyzLocal):
970 return Cls(*self, **_xkwds(Cls_kwds, name=self.name))
971 else:
972 return _4Tuple2Cls(self, Cls, Cls_kwds)
974 @Property_RO
975 def xyzLocal(self):
976 '''Get this L{Enu4Tuple} as an L{XyzLocal}.
977 '''
978 return XyzLocal(*self, name=self.name)
981class Local9Tuple(_NamedTuple):
982 '''9-Tuple C{(x, y, z, lat, lon, height, ltp, ecef, M)} with I{local} C{x},
983 C{y}, C{z} all in C{meter}, I{geodetic} C{lat}, C{lon}, C{height}, I{local
984 tangent plane} C{ltp} (L{Ltp}), C{ecef} (L{Ecef9Tuple}) with I{geocentric}
985 C{x}, C{y}, C{z}, I{geodetic} C{lat}, C{lon}, C{height} and I{concatenated}
986 rotation matrix C{M} (L{EcefMatrix}) or C{None}.
987 '''
988 _Names_ = (_x_, _y_, _z_, _lat_, _lon_, _height_, _ltp_, _ecef_, _M_)
989 _Units_ = ( Meter, Meter, Meter, Lat, Lon, Height, _Pass, _Pass, _Pass)
991 @Property_RO
992 def azimuth(self):
993 '''Get the I{local} Azimuth, bearing from North (C{degrees360}).
994 '''
995 return self.xyzLocal.aer4.azimuth
997 @Property_RO
998 def down(self):
999 '''Get the I{local} Down, C{-z} component (C{meter}).
1000 '''
1001 return -self.z
1003 @Property_RO
1004 def east(self):
1005 '''Get the I{local} East, C{x} component (C{meter}).
1006 '''
1007 return self.x
1009 @Property_RO
1010 def elevation(self):
1011 '''Get the I{local} Elevation, tilt I{above} horizon (C{degrees90}).
1012 '''
1013 return self.xyzLocal.aer4.elevation
1015 @Property_RO
1016 def groundrange(self):
1017 '''Get the I{local} ground range, distance (C{meter}).
1018 '''
1019 return self.xyzLocal.aer4.groundrange
1021 @Property_RO
1022 def lam(self):
1023 '''Get the I{geodetic} longitude in C{radians} (C{float}).
1024 '''
1025 return self.philam.lam
1027 @Property_RO
1028 def latlon(self):
1029 '''Get the I{geodetic} lat-, longitude in C{degrees} (L{LatLon2Tuple}C{(lat, lon)}).
1030 '''
1031 return LatLon2Tuple(self.lat, self.lon, name=self.name)
1033 @Property_RO
1034 def latlonheight(self):
1035 '''Get the I{geodetic} lat-, longitude in C{degrees} and height (L{LatLon3Tuple}C{(lat, lon, height)}).
1036 '''
1037 return self.latlon.to3Tuple(self.height)
1039 @Property_RO
1040 def north(self):
1041 '''Get the I{local} North, C{y} component (C{meter}).
1042 '''
1043 return self.y
1045 @Property_RO
1046 def phi(self):
1047 '''Get the I{geodetic} latitude in C{radians} (C{float}).
1048 '''
1049 return self.philam.phi
1051 @Property_RO
1052 def philam(self):
1053 '''Get the I{geodetic} lat-, longitude in C{radians} (L{PhiLam2Tuple}C{(phi, lam)}).
1054 '''
1055 return PhiLam2Tuple(radians(self.lat), radians(self.lon), name=self.name)
1057 @Property_RO
1058 def philamheight(self):
1059 '''Get the I{geodetic} lat-, longitude in C{radians} and height (L{PhiLam3Tuple}C{(phi, lam, height)}).
1060 '''
1061 return self.philam.to3Tuple(self.height)
1063 @Property_RO
1064 def slantrange(self):
1065 '''Get the I{local} slant Range, distance (C{meter}).
1066 '''
1067 return self.xyzLocal.aer4.slantrange
1069 def toAer(self, Aer=None, **Aer_kwds):
1070 '''Get the I{local} I{Azimuth, Elevation, slant Range} (AER) components.
1072 @kwarg Aer: Class to return AER (L{Aer}) or C{None}.
1073 @kwarg Aer_kwds: Optional, additional B{L{Aer}} keyword
1074 arguments, ignored if B{C{Aer}} is C{None}.
1076 @return: AER as an L{Aer} instance or if C{B{Aer} is None},
1077 an L{Aer4Tuple}C{(azimuth, elevation, slantrange, ltp)}.
1078 '''
1079 return self.xyzLocal.toAer(Aer=Aer, **Aer_kwds)
1081 def toCartesian(self, Cartesian=None, **Cartesian_kwds):
1082 '''Convert this I{local} to I{geocentric} C{(x, y, z)} (ECEF).
1084 @kwarg Cartesian: Optional class to return C{(x, y, z)} (C{Cartesian})
1085 or C{None}.
1086 @kwarg Cartesian_kwds: Optional, additional B{C{Cartesian}} keyword
1087 arguments, ignored if C{B{Cartesian} is None}.
1089 @return: A C{B{Cartesian}(x, y, z, **B{Cartesian_kwds})} instance
1090 or a L{Vector4Tuple}C{(x, y, z, h)} if C{B{Cartesian} is None}.
1092 @raise TypeError: Invalid B{C{Cartesian}} or B{C{Cartesian_kwds}}
1093 argument.
1094 '''
1095 return self.ecef.toCartesian(Cartesian=Cartesian, **Cartesian_kwds) # PYCHOK _Tuple
1097 def toEnu(self, Enu=None, **Enu_kwds):
1098 '''Get the I{local} I{East, North, Up} (ENU) components.
1100 @kwarg Enu: Class to return ENU (L{Enu}) or C{None}.
1101 @kwarg Enu_kwds: Optional, additional B{L{Enu}} keyword
1102 arguments, ignored if C{B{Enu} is None}.
1104 @return: ENU as an L{Enu} instance or if C{B{Enu} is None},
1105 an L{Enu4Tuple}C{(east, north, up, ltp)}.
1106 '''
1107 return self.xyzLocal.toEnu(Enu=Enu, **Enu_kwds)
1109 def toLatLon(self, LatLon=None, **LatLon_kwds):
1110 '''Convert this I{local} to I{geodetic} C{(lat, lon, height)}.
1112 @kwarg LatLon: Optional class to return C{(lat, lon, height)}
1113 (C{LatLon}) or C{None}.
1114 @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword
1115 arguments, ignored if C{B{LatLon} is None}.
1117 @return: An instance of C{B{LatLon}(lat, lon, **B{LatLon_kwds})}
1118 or if C{B{LatLon} is None}, a L{LatLon3Tuple}C{(lat, lon,
1119 height)} respectively L{LatLon4Tuple}C{(lat, lon, height,
1120 datum)} depending on whether C{datum} is un-/specified.
1122 @raise TypeError: Invalid B{C{LatLon}} or B{C{LatLon_kwds}}
1123 argument.
1124 '''
1125 return self.ecef.toLatLon(LatLon=LatLon, **LatLon_kwds) # PYCHOK _Tuple
1127 def toNed(self, Ned=None, **Ned_kwds):
1128 '''Get the I{local} I{North, East, Down} (NED) components.
1130 @kwarg Ned: Class to return NED (L{Ned}) or C{None}.
1131 @kwarg Ned_kwds: Optional, additional B{L{Ned}} keyword
1132 arguments, ignored if B{C{Ned}} is C{None}.
1134 @return: NED as an L{Ned} instance or if C{B{Ned} is None},
1135 an L{Ned4Tuple}C{(north, east, down, ltp)}.
1136 '''
1137 return self.xyzLocal.toNed(Ned=Ned, **Ned_kwds)
1139 def toXyz(self, Xyz=None, **Xyz_kwds):
1140 '''Get the I{local} I{X, Y, Z} (XYZ) components.
1142 @kwarg Xyz: Class to return XYZ (L{XyzLocal}) or C{None}.
1143 @kwarg Xyz_kwds: Optional, additional B{C{Xyz}} keyword
1144 arguments, ignored if C{B{Xyz} is None}.
1146 @return: XYZ as an B{C{Xyz}} instance or if C{B{Xyz} is None},
1147 an L{Xyz4Tuple}C{(x, y, z, ltp)}.
1148 '''
1149 return self.xyzLocal.toXyz(Xyz=Xyz, **Xyz_kwds)
1151 @Property_RO
1152 def up(self):
1153 '''Get the I{local} Up, C{z} component (C{meter}).
1154 '''
1155 return self.z
1157 @Property_RO
1158 def xyz(self):
1159 '''Get the I{local} C{(X, Y, Z)} components (L{Vector3Tuple}C{(x, y, z)}).
1160 '''
1161 return Vector3Tuple(self.x, self.y, self.z, name=self.name) # like Ecef9Tuple.xyz
1163 @Property_RO
1164 def xyzLocal(self):
1165 '''Get this L{Local9Tuple} as an L{XyzLocal}.
1166 '''
1167 return XyzLocal(self, name=self.name)
1170_XyzLocals4 = XyzLocal, Enu, Ned, Aer # PYCHOK in .ltp
1171_XyzLocals5 = _XyzLocals4 + (Local9Tuple,) # PYCHOK in .ltp
1174class ChLV9Tuple(Local9Tuple):
1175 '''9-Tuple C{(Y, X, h_, lat, lon, height, ltp, ecef, M)} with I{B{unfalsed} Swiss
1176 (Y, X, h_)} coordinates and height, all in C{meter}, C{ltp} either a L{ChLV},
1177 L{ChLVa} or L{ChLVe} instance and C{ecef} (L{EcefKarney} I{at Bern, Ch},
1178 otherwise like L{Local9Tuple}.
1179 '''
1180 _Names_ = (_Y_, _X_, _h__) + Local9Tuple._Names_[3:]
1182 @Property_RO
1183 def E_LV95(self):
1184 '''Get the B{falsed} I{Swiss E_LV95} easting (C{meter}).
1185 '''
1186 return self.EN2_LV95.E_LV95
1188 @Property_RO
1189 def EN2_LV95(self):
1190 '''Get the I{falsed Swiss (E_LV95, N_LV95)} easting and northing (L{ChLVEN2Tuple}).
1191 '''
1192 return ChLVEN2Tuple(*_MODS.ltp.ChLV.false2(self.Y, self.X, True), name=self.name)
1194 @Property_RO
1195 def h_LV03(self):
1196 '''Get the I{Swiss h_} height (C{meter}).
1197 '''
1198 return self.h_
1200 @Property_RO
1201 def h_LV95(self):
1202 '''Get the I{Swiss h_} height (C{meter}).
1203 '''
1204 return self.h_
1206 @property_RO
1207 def isChLV(self):
1208 '''Is this a L{ChLV}-generated L{ChLV9Tuple}?.
1209 '''
1210 return self.ltp.__class__ is _MODS.ltp.ChLV
1212 @property_RO
1213 def isChLVa(self):
1214 '''Is this a L{ChLVa}-generated L{ChLV9Tuple}?.
1215 '''
1216 return self.ltp.__class__ is _MODS.ltp.ChLVa
1218 @property_RO
1219 def isChLVe(self):
1220 '''Is this a L{ChLVe}-generated L{ChLV9Tuple}?.
1221 '''
1222 return self.ltp.__class__ is _MODS.ltp.ChLVe
1224 @Property_RO
1225 def N_LV95(self):
1226 '''Get the B{falsed} I{Swiss N_LV95} northing (C{meter}).
1227 '''
1228 return self.EN2_LV95.N_LV95
1230 @Property_RO
1231 def x(self):
1232 '''Get the I{local x, Swiss Y} easting (C{meter}).
1233 '''
1234 return self.Y
1236 @Property_RO
1237 def x_LV03(self):
1238 '''Get the B{falsed} I{Swiss x_LV03} northing (C{meter}).
1239 '''
1240 return self.yx2_LV03.x_LV03
1242 @Property_RO
1243 def y(self):
1244 '''Get the I{local y, Swiss X} northing (C{meter}).
1245 '''
1246 return self.X
1248 @Property_RO
1249 def y_LV03(self):
1250 '''Get the B{falsed} I{Swisss y_LV03} easting (C{meter}).
1251 '''
1252 return self.yx2_LV03.y_LV03
1254 @Property_RO
1255 def YX(self):
1256 '''Get the B{unfalsed} easting and northing (L{ChLVYX2Tuple}).
1257 '''
1258 return ChLVYX2Tuple(self.Y, self.X, name=self.name)
1260 @Property_RO
1261 def yx2_LV03(self):
1262 '''Get the B{falsed} I{Swiss (y_LV03, x_LV03)} easting and northing (L{ChLVyx2Tuple}).
1263 '''
1264 return ChLVyx2Tuple(*_MODS.ltp.ChLV.false2(self.Y, self.X, False), name=self.name)
1266 @Property_RO
1267 def z(self):
1268 '''Get the I{local z, Swiss h_} height (C{meter}).
1269 '''
1270 return self.h_
1273class ChLVYX2Tuple(_NamedTuple):
1274 '''2-Tuple C{(Y, X)} with B{unfalsed} I{Swiss LV95} easting and norting
1275 in C{meter}.
1276 '''
1277 _Names_ = (_Y_, _X_)
1278 _Units_ = ( Meter, Meter)
1280 def false2(self, LV95=True):
1281 '''Return the falsed C{Swiss LV95} or C{LV03} version of the projection.
1283 @see: Function L{ChLV.false2} for more information.
1284 '''
1285 return _MODS.ltp.ChLV.false2(*self, LV95=LV95, name=self.name)
1288class ChLVEN2Tuple(_NamedTuple):
1289 '''2-Tuple C{(E_LV95, N_LV95)} with B{falsed} I{Swiss LV95} easting and
1290 norting in C{meter (2_600_000, 1_200_000)} and origin at C{Bern, Ch}.
1291 '''
1292 _Names_ = ('E_LV95', 'N_LV95')
1293 _Units_ = ChLVYX2Tuple._Units_
1295 def unfalse2(self):
1296 '''Return this projection as an B{unfalsed} L{ChLVYX2Tuple}.
1298 @see: Function L{ChLV.unfalse2} for more information.
1299 '''
1300 return _MODS.ltp.ChLV.unfalse2(*self, LV95=True, name=self.name)
1303class ChLVyx2Tuple(_NamedTuple):
1304 '''2-Tuple C{(y_LV03, x_LV03)} with B{falsed} I{Swiss LV03} easting and
1305 norting in C{meter (600_000, 200_000)} and origin at C{Bern, Ch}.
1306 '''
1307 _Names_ = ('y_LV03', 'x_LV03')
1308 _Units_ = ChLVYX2Tuple._Units_
1310 def unfalse2(self):
1311 '''Return this projection as an B{unfalsed} L{ChLVYX2Tuple}.
1313 @see: Function L{ChLV.unfalse2} for more information.
1314 '''
1315 return _MODS.ltp.ChLV.unfalse2(*self, LV95=False, name=self.name)
1318class Footprint5Tuple(_NamedTuple):
1319 '''5-Tuple C{(center, upperleft, upperight, loweright, lowerleft)}
1320 with the C{center} and 4 corners of the I{local} projection of
1321 a C{Frustum}, each an L{Xyz4Tuple}, L{XyzLocal}, C{LatLon}, etc.
1323 @note: Misspelling of C{upperight} and C{loweright} is I{intentional}.
1324 '''
1325 _Names_ = (_center_, 'upperleft', 'upperight', 'loweright', 'lowerleft')
1326 _Units_ = (_Pass, _Pass, _Pass, _Pass, _Pass)
1328 def toLatLon5(self, ltp=None, LatLon=None, **LatLon_kwds):
1329 '''Convert this footprint's C{center} and 4 corners to I{geodetic}
1330 C{LatLon(lat, lon, height)}s or C{LatLon3-} or C{-4Tuple}s.
1332 @kwarg ltp: The I{local tangent plane} (L{Ltp}), overriding this
1333 footprint's C{center} or C{frustrum} C{ltp}.
1334 @kwarg LatLon: Optional I{geodetic} class (C{LatLon}) or C{None}.
1335 @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword
1336 arguments, ignored if C{B{LatLon} is None}.
1338 @return: A L{Footprint5Tuple} of 5 C{B{LatLon}(lat, lon,
1339 **B{LatLon_kwds})} instances or if C{B{LatLon} is None},
1340 5 L{LatLon3Tuple}C{(lat, lon, height)}s respectively
1341 5 L{LatLon4Tuple}C{(lat, lon, height, datum)}s depending
1342 on keyword argument C{datum} is un-/specified.
1344 @raise TypeError: Invalid B{C{ltp}}, B{C{LatLon}} or B{C{LatLon_kwds}}.
1346 @see: Methods L{XyzLocal.toLatLon} and L{Footprint5Tuple.xyzLocal5}.
1347 '''
1348 kwds = _xkwds(LatLon_kwds, ltp=_MODS.ltp._xLtp(ltp, self.center.ltp), # PYCHOK .center
1349 LatLon=LatLon, name=self.name,)
1350 return Footprint5Tuple(t.toLatLon(**kwds) for t in self.xyzLocal5())
1352 def xyzLocal5(self, ltp=None):
1353 '''Return this footprint's C{center} and 4 corners as 5 L{XyzLocal}s.
1355 @kwarg ltp: The I{local tangent plane} (L{Ltp}), overriding
1356 the {center} and corner C{ltp}s.
1358 @return: A L{Footprint5Tuple} of 5 L{XyzLocal} instances.
1360 @raise TypeError: Invalid B{C{ltp}}.
1361 '''
1362 if ltp is None:
1363 p = self
1364 else:
1365 p = _MODS.ltp._xLtp(ltp)
1366 p = tuple(Xyz4Tuple(t.x, t.y, t.z, p) for t in self)
1367 return Footprint5Tuple(t.xyzLocal for t in p)
1370__all__ += _ALL_DOCS(_NamedAerNed)
1372# **) MIT License
1373#
1374# Copyright (C) 2016-2023 -- mrJean1 at Gmail -- All Rights Reserved.
1375#
1376# Permission is hereby granted, free of charge, to any person obtaining a
1377# copy of this software and associated documentation files (the "Software"),
1378# to deal in the Software without restriction, including without limitation
1379# the rights to use, copy, modify, merge, publish, distribute, sublicense,
1380# and/or sell copies of the Software, and to permit persons to whom the
1381# Software is furnished to do so, subject to the following conditions:
1382#
1383# The above copyright notice and this permission notice shall be included
1384# in all copies or substantial portions of the Software.
1385#
1386# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1387# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1388# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1389# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
1390# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
1391# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
1392# OTHER DEALINGS IN THE SOFTWARE.