1

2# -*- coding: utf-8 -*-

3

4 u'''Named, I{Local Tangent Plane} (LTP) tuples.

5

6 Local coordinate classes L{XyzLocal}, L{Enu}, L{Ned} and L{Aer}

7 and local coordinate tuples L{Local9Tuple}, L{Xyz4Tuple}, L{Enu4Tuple},

8 L{Ned4Tuple}, L{Aer4Tuple}, L{ChLV9Tuple}, L{ChLVEN2Tuple},

9 L{ChLVYX2Tuple}, L{ChLVyx2Tuple} and L{Footprint5Tuple}.

10

11@see: References in module L{ltp}.

12'''

13

14 from pygeodesy.basics import issubclassof, _xinstanceof

15 from pygeodesy.constants import _0_0, _90_0, _N_90_0

16 from pygeodesy.dms import F_D, toDMS

17 from pygeodesy.errors import _TypesError, _xattr, _xkwds

18 from pygeodesy.fmath import hypot, hypot_

19 from pygeodesy.interns import NN, _4_, _azimuth_, _center_, _COMMASPACE_, \

20 _down_, _east_, _ecef_, _elevation_, _height_, \

21 _lat_, _lon_, _ltp_, _M_, _north_, _up_, _X_, \

22 _x_, _xyz_, _Y_, _y_, _z_

23 from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS

24 from pygeodesy.named import _NamedBase, _NamedTuple, notOverloaded, \

25 _Pass, _xnamed

26 from pygeodesy.namedTuples import LatLon2Tuple, PhiLam2Tuple, Vector3Tuple

27 from pygeodesy.props import deprecated_method, deprecated_Property_RO, \

28 Property_RO, property_RO

29 from pygeodesy.streprs import Fmt, fstr, strs, _xzipairs

30 from pygeodesy.units import Bearing, Degrees, Degrees_, Height, Lat, Lon, \

31 Meter, Meter_

32 from pygeodesy.utily import atan2d, atan2b, sincos2d_

33 from pygeodesy.vector3d import Vector3d

34

35 from math import cos, radians

36

37__all__ = _ALL_LAZY.ltpTuples

38__version__ = '23.05.06'

39

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'

49

50

51 def _er2gr(e, r):

52 '''(INTERNAL) Elevation and slant range to ground range.

53 '''

54 c = cos(radians(e))

55 return Meter_(groundrange=r * c)

56

57

58 def _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

69

70

71 def _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)

88

89

90 def _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)

98

99

100 class _NamedAerNed(_NamedBase):

101 '''(INTERNAL) Base class for classes C{Aer} and C{Ned}.

102 '''

103 _ltp = None # local tangent plane (C{Ltp}), origin

104

105 @Property_RO

106 def ltp(self):

107 '''Get the I{local tangent plane} (L{Ltp}).

108 '''

109 return self._ltp

110

111 def toAer(self, Aer=None, **Aer_kwds):

112 '''Get the I{local} I{Azimuth, Elevation, slant Range} (AER) components.

113

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

117

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)

122

123 def toEnu(self, Enu=None, **Enu_kwds):

124 '''Get the I{local} I{East, North, Up} (ENU) components.

125

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

129

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)

134

135 def toNed(self, Ned=None, **Ned_kwds):

136 '''Get the I{local} I{North, East, Down} (NED) components.

137

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

141

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)

146

147 def toXyz(self, Xyz=None, **Xyz_kwds):

148 '''Get the local I{X, Y, Z} (XYZ) components.

149

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

154

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)}.

157

158 @raise TypeError: Invalid B{C{Xyz}}.

159 '''

160 return self.xyz4._toXyz(Xyz, Xyz_kwds)

161

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

167

168 @property_RO

169 def xyz4(self):

170 '''(INTERNAL) I{Must be overloaded}, see function C{notOverloaded}.

171 '''

172 notOverloaded(self)

173

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)

179

180

181 class 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_

189

190 def __init__(self, aer, elevation=_0_0, slantrange=_0_0, ltp=None, name=NN):

191 '''New L{Aer}.

192

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}).

201

202 @raise TypeError: Invalid B{C{ltp}}.

203

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 = _xattr(aer, ltp=ltp)

212 n = _xattr(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

218

219 if p:

220 self._ltp = _MODS.ltp._xLtp(p)

221 if name:

222 self.name = n

223

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)

229

230 @Property_RO

231 def azimuth(self):

232 '''Get the Azimuth, bearing from North (C{degrees360}).

233 '''

234 return self._azimuth

235

236 @Property_RO

237 def down(self):

238 '''Get the Down component (C{meter}).

239 '''

240 return self.xyzLocal.down

241

242 @Property_RO

243 def east(self):

244 '''Get the East component (C{meter}).

245 '''

246 return self.xyzLocal.east

247

248 @Property_RO

249 def elevation(self):

250 '''Get the Elevation, tilt above horizon (C{degrees90}).

251 '''

252 return self._elevation

253

254 @Property_RO

255 def groundrange(self):

256 '''Get the I{ground range}, distance (C{meter}).

257 '''

258 return _er2gr(self._elevation, self._slantrange)

259

260 @Property_RO

261 def north(self):

262 '''Get the North component (C{meter}).

263 '''

264 return self.xyzLocal.north

265

266 @Property_RO

267 def slantrange(self):

268 '''Get the I{slant Range}, distance (C{meter}).

269 '''

270 return self._slantrange

271

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.

275

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}).

279

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)

286

287 def toStr(self, **prec_fmt_sep): # PYCHOK expected

288 '''Return a string representation of this AER.

289

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}).

295

296 @return: This AER as "[degrees360, degrees90, meter]" (C{str}).

297 '''

298 _, t = _toStr2(self, **prec_fmt_sep)

299 return t

300

301 @Property_RO

302 def up(self):

303 '''Get the Up component (C{meter}).

304 '''

305 return self.xyzLocal.up

306

307 @Property_RO

308 def x(self):

309 '''Get the X component (C{meter}).

310 '''

311 return self.xyz4.x

312

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)

321

322 @Property_RO

323 def y(self):

324 '''Get the Y component (C{meter}).

325 '''

326 return self.xyz4.y

327

328 @Property_RO

329 def z(self):

330 '''Get the Z component (C{meter}).

331 '''

332 return self.xyz4.z

333

334

335 class 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)

341

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)

349

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

355

356 @Property_RO

357 def xyzLocal(self):

358 '''Get this L{Aer4Tuple} as an L{XyzLocal}.

359 '''

360 return Aer(self).xyzLocal

361

362

363 class 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)

370

371 @Property_RO

372 def atyr(self):

373 '''Return this attitude (L{Attitude4Tuple}).

374 '''

375 return self

376

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

382

383

384 class Ned(_NamedAerNed):

385 '''Local C{North-Eeast-Down} (NED) location in a I{local tangent plane}.

386

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_

394

395 def __init__(self, ned, east=_0_0, down=_0_0, ltp=None, name=NN):

396 '''New L{Ned} vector.

397

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}).

406

407 @raise TypeError: Invalid B{C{ltp}}.

408

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 = _xattr(ned, ltp=ltp)

415 n = _xattr(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

421

422 if p:

423 self._ltp = _MODS.ltp._xLtp(p)

424 if n:

425 self.name = n

426

427 @Property_RO

428 def aer4(self):

429 '''Get the C{(azimuth, elevation, slantrange, ltp)} components (L{Aer4Tuple}).

430 '''

431 return _xyz2aer4(self)

432

433 @Property_RO

434 def azimuth(self):

435 '''Get the Azimuth, bearing from North (C{degrees360}).

436 '''

437 return self.aer4.azimuth

438

439 @deprecated_Property_RO

440 def bearing(self):

441 '''DEPRECATED, use C{azimuth}.'''

442 return self.azimuth

443

444 @Property_RO

445 def down(self):

446 '''Get the Down component (C{meter}).

447 '''

448 return self._down

449

450 @Property_RO

451 def east(self):

452 '''Get the East component (C{meter}).

453 '''

454 return self._east

455

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))))

461

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))

467

468 @deprecated_Property_RO

469 def length(self):

470 '''DEPRECATED, use C{slantrange}.'''

471 return self.slantrange

472

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)

477

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)

483

484 @Property_RO

485 def north(self):

486 '''Get the North component (C{meter}).

487 '''

488 return self._north

489

490 @Property_RO

491 def slantrange(self):

492 '''Get the I{slant Range}, distance (C{meter}).

493 '''

494 return self.aer4.slantrange

495

496 @deprecated_method

497 def to3ned(self): # PYCHOK no cover

498 '''DEPRECATED, use property L{ned4}.'''

499 return self.ned # XXX deprecated too

500

501 def toRepr(self, prec=None, fmt=Fmt.SQUARE, sep=_COMMASPACE_, **unused): # PYCHOK expected

502 '''Return a string representation of this NED.

503

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}).

507

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)

512

513 def toStr(self, **prec_fmt_sep): # PYCHOK expected

514 '''Return a string representation of this NED.

515

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}).

521

522 @return: This NED as "[meter, meter, meter]" (C{str}).

523 '''

524 _, t = _toStr2(self, **prec_fmt_sep)

525 return t

526

527 @deprecated_method

528 def toVector3d(self):

529 '''DEPRECATED, use property L{xyz}.'''

530 return self.xyz

531

532 @Property_RO

533 def up(self):

534 '''Get the Up component (C{meter}).

535 '''

536 return Meter(up=-self._down) # negated

537

538 @Property_RO

539 def x(self):

540 '''Get the X component (C{meter}).

541 '''

542 return Meter(x=self._east) # 2nd arg, E

543

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)

549

550 @Property_RO

551 def y(self):

552 '''Get the Y component (C{meter}).

553 '''

554 return Meter(y=self._north) # 1st arg N

555

556 @Property_RO

557 def z(self):

558 '''Get the Z component (C{meter}).

559 '''

560 return Meter(z=-self._down) # negated

561

562

563 class 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)

568

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)

576

577 @Property_RO

578 def xyzLocal(self):

579 '''Get this L{Ned4Tuple} as an L{XyzLocal}.

580 '''

581 return Ned(self).xyzLocal

582

583

584 class 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_

590

591 def __init__(self, x_xyz, y=_0_0, z=_0_0, ltp=None, name=NN):

592 '''New L{XyzLocal}.

593

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}).

604

605 @raise TypeError: Invalid B{C{x_xyz}} or B{C{ltp}}.

606

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 = _xattr(x_xyz, ltp=ltp)

614 n = name or _xattr(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

620

621 if p:

622 self._ltp = _MODS.ltp._xLtp(p)

623 if n:

624 self.name = n

625

626 def __str__(self):

627 return self.toStr()

628

629 @Property_RO

630 def aer4(self):

631 '''Get the C{(azimuth, elevation, slantrange, ltp)} components (L{Aer4Tuple}).

632 '''

633 return _xyz2aer4(self)

634

635 @Property_RO

636 def azimuth(self):

637 '''Get the Azimuth, bearing from North (C{degrees360}).

638

639 @see: U{Azimuth<https://GSSC.ESA.int/navipedia/index.php/

640 Transformations_between_ECEF_and_ENU_coordinates>}.

641 '''

642 return self.aer4.azimuth

643

644 def classof(self, *args, **kwds): # PYCHOK no cover

645 '''Create another instance of this very class.

646

647 @arg args: Optional, positional arguments.

648 @kwarg kwds: Optional, keyword arguments.

649

650 @return: New instance (C{self.__class__}).

651 '''

652 kwds = _xkwds(kwds, ltp=self.ltp, name=self.name)

653 return self.__class__(*args, **kwds)

654

655 @Property_RO

656 def down(self):

657 '''Get the Down component (C{meter}).

658 '''

659 return Meter(down=-self.z)

660

661 @property_RO

662 def ecef(self):

663 '''Get this LTP's ECEF converter (C{Ecef...} I{instance}).

664 '''

665 return self.ltp.ecef

666

667 @Property_RO

668 def east(self):

669 '''Get the East component (C{meter}).

670 '''

671 return Meter(east=self.x)

672

673 @Property_RO

674 def elevation(self):

675 '''Get the Elevation, tilt above horizon (C{degrees90}).

676

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))))

681

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)

687

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))

693

694 @Property_RO

695 def ltp(self):

696 '''Get the I{local tangent plane} (L{Ltp}).

697 '''

698 return self._ltp

699

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)

705

706 @Property_RO

707 def north(self):

708 '''Get the North component (C{meter}).

709 '''

710 return Meter(north=self.y)

711

712 @Property_RO

713 def slantrange(self):

714 '''Get the I{slant Range}, distance (C{meter}).

715 '''

716 return self.aer4.slantrange

717

718 def toAer(self, Aer=None, **Aer_kwds):

719 '''Get the local I{Azimuth, Elevation, slantRange} components.

720

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

724

725 @return: AER as an L{Aer} instance or if C{B{Aer} is None}, an

726 L{Aer4Tuple}C{(azimuth, elevation, slantrange, ltp)}.

727

728 @raise TypeError: Invalid B{C{Aer}}.

729 '''

730 return self.aer4._toAer(Aer, Aer_kwds)

731

732 def toCartesian(self, Cartesian=None, ltp=None, **Cartesian_kwds):

733 '''Get the geocentric C{(x, y, z)} (ECEF) coordinates of this local.

734

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

741

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.

745

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)

757

758 def toEnu(self, Enu=None, **Enu_kwds):

759 '''Get the local I{East, North, Up} (ENU) components.

760

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

764

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)

769

770 def toLatLon(self, LatLon=None, ltp=None, **LatLon_kwds):

771 '''Get the geodetic C{(lat, lon, height)} coordinates if this local.

772

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

779

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.

783

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

796

797 def toLocal9Tuple(self, M=False, name=NN):

798 '''Get this local as a C{Local9Tuple}.

799

800 @kwarg M: Optionally include the rotation matrix (C{bool}).

801 @kwarg name: Optional name (C{str}).

802

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)

811

812 def toNed(self, Ned=None, **Ned_kwds):

813 '''Get the local I{North, East, Down} (Ned) components.

814

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

818

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)

823

824 def toRepr(self, prec=None, fmt=Fmt.SQUARE, sep=_COMMASPACE_, **unused): # PYCHOK expected

825 '''Return a string representation of this ENU/NED/XYZ.

826

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}).

830

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)

837

838 def toStr(self, **prec_fmt_sep): # PYCHOK expected

839 '''Return a string representation of this XYZ.

840

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}).

846

847 @return: This XYZ as "[meter, meter, meter]" (C{str}).

848 '''

849 _, t = _toStr2(self, **prec_fmt_sep)

850 return t

851

852 def toXyz(self, Xyz=None, **Xyz_kwds):

853 '''Get the local I{X, Y, Z} (XYZ) components.

854

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

859

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)

864

865 @Property_RO

866 def up(self):

867 '''Get the Up component (C{meter}).

868 '''

869 return Meter(up=self.z)

870

871# @Property_RO

872# def x(self): # see: Vector3d.x

873# '''Get the X component (C{meter}).

874# '''

875# return self._x

876

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

882

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)

888

889 @Property_RO

890 def xyzLocal(self):

891 '''Get this L{XyzLocal}.

892 '''

893 return self

894

895# @Property_RO

896# def y(self): # see: Vector3d.y

897# '''Get the Y component (C{meter}).

898# '''

899# return self._y

900

901# @Property_RO

902# def z(self): # see: Vector3d.z

903# '''Get the Z component (C{meter}).

904# '''

905# return self._z

906

907

908 class 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)

913

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)

921

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

927

928

929 class Enu(XyzLocal):

930 '''Local C{Eeast-North-Up} (ENU) location in a I{local tangent plane}.

931

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_

936

937 def __init__(self, enu, north=_0_0, up=_0_0, ltp=None, name=NN):

938 '''New L{Enu}.

939

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}).

948

949 @raise TypeError: Invalid B{C{enu}} or B{C{ltp}}.

950

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)

954

955# @Property_RO

956# def xyzLocal(self):

957# return XyzLocal(self, name=self.name)

958

959

960 class 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)

965

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)

973

974 @Property_RO

975 def xyzLocal(self):

976 '''Get this L{Enu4Tuple} as an L{XyzLocal}.

977 '''

978 return XyzLocal(*self, name=self.name)

979

980

981 class 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)

990

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

996

997 @Property_RO

998 def down(self):

999 '''Get the I{local} Down, C{-z} component (C{meter}).

1000 '''

1001 return -self.z

1002

1003 @Property_RO

1004 def east(self):

1005 '''Get the I{local} East, C{x} component (C{meter}).

1006 '''

1007 return self.x

1008

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

1014

1015 @Property_RO

1016 def groundrange(self):

1017 '''Get the I{local} ground range, distance (C{meter}).

1018 '''

1019 return self.xyzLocal.aer4.groundrange

1020

1021 @Property_RO

1022 def lam(self):

1023 '''Get the I{geodetic} longitude in C{radians} (C{float}).

1024 '''

1025 return self.philam.lam

1026

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)

1032

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)

1038

1039 @Property_RO

1040 def north(self):

1041 '''Get the I{local} North, C{y} component (C{meter}).

1042 '''

1043 return self.y

1044

1045 @Property_RO

1046 def phi(self):

1047 '''Get the I{geodetic} latitude in C{radians} (C{float}).

1048 '''

1049 return self.philam.phi

1050

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)

1056

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)

1062

1063 @Property_RO

1064 def slantrange(self):

1065 '''Get the I{local} slant Range, distance (C{meter}).

1066 '''

1067 return self.xyzLocal.aer4.slantrange

1068

1069 def toAer(self, Aer=None, **Aer_kwds):

1070 '''Get the I{local} I{Azimuth, Elevation, slant Range} (AER) components.

1071

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

1075

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)

1080

1081 def toCartesian(self, Cartesian=None, **Cartesian_kwds):

1082 '''Convert this I{local} to I{geocentric} C{(x, y, z)} (ECEF).

1083

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

1088

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

1091

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

1096

1097 def toEnu(self, Enu=None, **Enu_kwds):

1098 '''Get the I{local} I{East, North, Up} (ENU) components.

1099

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

1103

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)

1108

1109 def toLatLon(self, LatLon=None, **LatLon_kwds):

1110 '''Convert this I{local} to I{geodetic} C{(lat, lon, height)}.

1111

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

1116

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.

1121

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

1126

1127 def toNed(self, Ned=None, **Ned_kwds):

1128 '''Get the I{local} I{North, East, Down} (NED) components.

1129

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

1133

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)

1138

1139 def toXyz(self, Xyz=None, **Xyz_kwds):

1140 '''Get the I{local} I{X, Y, Z} (XYZ) components.

1141

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

1145

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)

1150

1151 @Property_RO

1152 def up(self):

1153 '''Get the I{local} Up, C{z} component (C{meter}).

1154 '''

1155 return self.z

1156

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

1162

1163 @Property_RO

1164 def xyzLocal(self):

1165 '''Get this L{Local9Tuple} as an L{XyzLocal}.

1166 '''

1167 return XyzLocal(self, name=self.name)

1168

1169

1170_XyzLocals4 = XyzLocal, Enu, Ned, Aer # PYCHOK in .ltp

1171_XyzLocals5 = _XyzLocals4 + (Local9Tuple,) # PYCHOK in .ltp

1172

1173

1174 class 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:]

1181

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

1187

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)

1193

1194 @Property_RO

1195 def h_LV03(self):

1196 '''Get the I{Swiss h_} height (C{meter}).

1197 '''

1198 return self.h_

1199

1200 @Property_RO

1201 def h_LV95(self):

1202 '''Get the I{Swiss h_} height (C{meter}).

1203 '''

1204 return self.h_

1205

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

1211

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

1217

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

1223

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

1229

1230 @Property_RO

1231 def x(self):

1232 '''Get the I{local x, Swiss Y} easting (C{meter}).

1233 '''

1234 return self.Y

1235

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

1241

1242 @Property_RO

1243 def y(self):

1244 '''Get the I{local y, Swiss X} northing (C{meter}).

1245 '''

1246 return self.X

1247

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

1253

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)

1259

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)

1265

1266 @Property_RO

1267 def z(self):

1268 '''Get the I{local z, Swiss h_} height (C{meter}).

1269 '''

1270 return self.h_

1271

1272

1273 class 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)

1279

1280 def false2(self, LV95=True):

1281 '''Return the falsed C{Swiss LV95} or C{LV03} version of the projection.

1282

1283 @see: Function L{ChLV.false2} for more information.

1284 '''

1285 return _MODS.ltp.ChLV.false2(*self, LV95=LV95, name=self.name)

1286

1287

1288 class 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_

1294

1295 def unfalse2(self):

1296 '''Return this projection as an B{unfalsed} L{ChLVYX2Tuple}.

1297

1298 @see: Function L{ChLV.unfalse2} for more information.

1299 '''

1300 return _MODS.ltp.ChLV.unfalse2(*self, LV95=True, name=self.name)

1301

1302

1303 class 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_

1309

1310 def unfalse2(self):

1311 '''Return this projection as an B{unfalsed} L{ChLVYX2Tuple}.

1312

1313 @see: Function L{ChLV.unfalse2} for more information.

1314 '''

1315 return _MODS.ltp.ChLV.unfalse2(*self, LV95=False, name=self.name)

1316

1317

1318 class 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.

1322

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)

1327

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.

1331

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

1337

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.

1343

1344 @raise TypeError: Invalid B{C{ltp}}, B{C{LatLon}} or B{C{LatLon_kwds}}.

1345

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())

1351

1352 def xyzLocal5(self, ltp=None):

1353 '''Return this footprint's C{center} and 4 corners as 5 L{XyzLocal}s.

1354

1355 @kwarg ltp: The I{local tangent plane} (L{Ltp}), overriding

1356 the {center} and corner C{ltp}s.

1357

1358 @return: A L{Footprint5Tuple} of 5 L{XyzLocal} instances.

1359

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)

1368

1369

1370__all__ += _ALL_DOCS(_NamedAerNed)

1371

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.