def toNvector(self, h=None, Nvector=None, **kwds): '''Convert this point to C{n-vector} (normal to the earth's surface) components, I{including height}. @keyword h: Optional height, overriding this point's height (C{meter}). @keyword Nvector: Optional (sub-)class to return the C{n-vector} components (C{Nvector}) or C{None}. @keyword kwds: Optional, additional B{C{Nvector}} keyword arguments, ignored if C{B{Nvector}=None}. @return: A B{C{Nvector}} or an L{Vector4Tuple}C{(x, y, z, h)} if C{B{Nvector}=None}. @raise TypeError: Invalid B{C{Nvector}} or B{C{kwds}}. ''' if kwds or h not in (None, self.height): r = Vector3Tuple(*self.toVector3d().to3xyz()) r = r._4Tuple(self.height if h is None else h) if Nvector is not None: r = Nvector(r.x, r.y, r.z, h=r.h, ll=self, **kwds) else: # self.height, no kwds r = self._v4t if r is None: r = Vector3Tuple(*self.toVector3d().to3xyz()) self._v4t = r = r._4Tuple(self.height) if Nvector is not None: r = Nvector(r.x, r.y, r.z, h=r.h, ll=self) return self._xnamed(r)
def parse3d(str3d, sep=_COMMA_, name=NN, Vector=Vector3d, **Vector_kwds): '''Parse an C{"x, y, z"} string. @arg str3d: X, y and z values (C{str}). @kwarg sep: Optional separator (C{str}). @kwarg name: Optional instance name (C{str}). @kwarg Vector: Optional class (L{Vector3d}). @kwarg Vector_kwds: Optional B{C{Vector}} keyword arguments, ignored if B{C{Vector=None}}. @return: New B{C{Vector}} or if B{C{Vector}} is C{None}, a L{Vector3Tuple}C{(x, y, z)}. @raise VectorError: Invalid B{C{str3d}}. ''' try: v = [float(v.strip()) for v in str3d.split(sep)] if len(v) != 3: raise ValueError except (TypeError, ValueError) as x: raise VectorError(str3d=str3d, txt=str(x)) r = Vector3Tuple(*v) if Vector is None else \ Vector(*v, **Vector_kwds) return _xnamed(r, name, force=True)
def to3xyz(self): # overloads LatLonBase.to3xyz # PYCHOK no cover '''DEPRECATED, use method C{toEcef}. @return: A L{Vector3Tuple}C{(x, y, z)}. ''' if self._3xyz is None: r = self.toEcef() self._3xyz = Vector3Tuple(r.x, r.y, r.z) return self._xrenamed(self._3xyz)
def to3xyz(self): # PYCHOK no cover '''DEPRECATED, use method C{toEcef}. @return: A L{Vector3Tuple}C{(x, y, z)}. @note: Overloads C{LatLonBase.to3xyz} ''' r = self.toEcef() return self._xnamed(Vector3Tuple(r.x, r.y, r.z))
def toVector(self, Vector=None): '''Return the geocentric C{(x, y, z)} coordinates as vector. @keyword Vector: Optional vector (sub-)class to return C{(x, y, z)} or C{None}. @return: A C{Vector}C{(x, y, z)} instance or if B{C{Vector}} is C{None} a L{Vector3Tuple}C{(x, y, z)}. ''' r = Vector3Tuple(self.x, self.y, self.z) if Vector is None else \ Vector(self.x, self.y, self.z) # PYCHOK x, y, z return self._xnamed(r)
def to3xyz(self): '''Convert this (geodetic) point to (n-)vector (normal to the earth's surface) x/y/z components, ignoring the height. @return: A L{Vector3Tuple}C{(x, y, z)} in C{units}, NOT C{meter}. ''' # Kenneth Gade eqn 3, but using right-handed # vector x -> 0°E,0°N, y -> 90°E,0°N, z -> 90°N a, b = self.to2ab() sa, ca, sb, cb = sincos2(a, b) r = Vector3Tuple(ca * cb, ca * sb, sa) return self._xnamed(r)
def philam2n_xyz(phi, lam): '''Convert lat-, longitude to C{n-vector} (normal to the earth's surface) X, Y and Z components. @arg phi: Latitude (C{radians}). @arg lam: Longitude (C{radians}). @return: A L{Vector3Tuple}C{(x, y, z)}. @see: Function L{latlon2n_xyz}. @note: These are C{n-vector} x, y and z components, I{NOT} geocentric ECEF x, y and z coordinates! ''' # Kenneth Gade eqn 3, but using right-handed # vector x -> 0°E,0°N, y -> 90°E,0°N, z -> 90°N sa, ca, sb, cb = sincos2(phi, lam) return Vector3Tuple(ca * cb, ca * sb, sa)
def sumOf(vectors, Vector=Vector3d, **kwds): '''Compute the vectorial sum of several vectors. @param vectors: Vectors to be added (L{Vector3d}[]). @keyword Vector: Optional class for the vectorial sum (L{Vector3d}). @keyword kwds: Optional, additional B{C{Vector}} keyword arguments, ignored if C{B{Vector}=None}. @return: Vectorial sum (B{C{Vector}}). @raise VectorError: No B{C{vectors}}. ''' n, vectors = len2(vectors) if n < 1: raise VectorError('no vectors: %r' & (n, )) r = Vector3Tuple(fsum(v.x for v in vectors), fsum(v.y for v in vectors), fsum(v.z for v in vectors)) if Vector is not None: r = Vector(r.x, r.y, r.z, **kwds) # PYCHOK x, y, z return r
def sumOf(vectors, Vector=Vector3d, **Vector_kwds): '''Compute the vectorial sum of several vectors. @arg vectors: Vectors to be added (L{Vector3d}[]). @kwarg Vector: Optional class for the vectorial sum (L{Vector3d}). @kwarg Vector_kwds: Optional B{C{Vector}} keyword arguments, ignored if B{C{Vector=None}}. @return: Vectorial sum (B{C{Vector}}). @raise VectorError: No B{C{vectors}}. ''' n, vectors = len2(vectors) if n < 1: raise VectorError(vectors=n, txt=_Missing) r = Vector3Tuple(fsum(v.x for v in vectors), fsum(v.y for v in vectors), fsum(v.z for v in vectors)) if Vector is not None: r = Vector(r.x, r.y, r.z, **Vector_kwds) # PYCHOK x, y, z return r
def toVector(self, Vector=None, **kwds): '''Convert this point to C{n-vector} (normal to the earth's surface) components, I{ignoring height}. @keyword Vector: Optional (sub-)class to return the C{n-vector} components (L{Vector3d}) or C{None}. @keyword kwds: Optional, additional B{C{Vector}} keyword arguments, ignored if C{B{Vector}=None}. @return: A B{C{Vector}} or if C{B{Vector}=None}, an L{Vector3Tuple}C{(x, y, z)}. @raise TypeError: Invalid B{C{Vector}} or B{C{kwds}}. ''' # Kenneth Gade eqn 3, but using right-handed # vector x -> 0°E,0°N, y -> 90°E,0°N, z -> 90°N a, b = self.to2ab() sa, ca, sb, cb = sincos2(a, b) r = Vector3Tuple(ca * cb, ca * sb, sa) if Vector is not None: r = Vector(r.x, r.y, r.z, **kwds) # PYCHOK Vector3Tuple return self._xnamed(r)
def transform(self, x, y, z, inverse=False): '''Transform a (geocentric) Cartesian point, forward or inverse. @arg x: X coordinate (C{meter}). @arg y: Y coordinate (C{meter}). @arg z: Z coordinate (C{meter}). @kwarg inverse: Optional direction, forward or inverse (C{bool}). @return: A L{Vector3Tuple}C{(x, y, z)}, transformed. ''' if inverse: _xyz = -1, -x, -y, -z _s1 = self.s1 - 2 # == -(1 - s * 1e-6)) == -(1 - (s1 - 1)) else: _xyz = 1, x, y, z _s1 = self.s1 # x', y', z' = (.tx + x * .s1 - y * .rz + z * .ry, # .ty + x * .rz + y * .s1 - z * .rx, # .tz - x * .ry + y * .rx + z * .s1) r = Vector3Tuple(fdot(_xyz, self.tx, _s1, -self.rz, self.ry), fdot(_xyz, self.ty, self.rz, _s1, -self.rx), fdot(_xyz, self.tz, -self.ry, self.rx, _s1)) return self._xnamed(r)
def to3xyz(self): # overloads _LatLonHeightBase.to3xyz '''Convert this (ellipsoidal) geodetic C{LatLon} point to (geocentric) cartesian x/y/z components. @return: A L{Vector3Tuple}C{(x, y, z)}. ''' if self._3xyz is None: a, b = self.to2ab() sa, ca, sb, cb = sincos2(a, b) E = self.ellipsoid() # radius of curvature in prime vertical t = E.e2s2(sa) # r, _ = E.roc2_(sa, 1) if t < EPS: r = 0 elif t > EPS1: r = E.a else: r = E.a / sqrt(t) h = self.height t = (h + r) * ca self._3xyz = Vector3Tuple(t * cb, t * sb, (h + r * E.e12) * sa) return self._xrenamed(self._3xyz)
def to3xyz(self): '''Return this vector as a 3-tuple. @return: A L{Vector3Tuple}C{(x, y, z)}. ''' return self._xnamed(Vector3Tuple(self.x, self.y, self.z))
def testCartesian(self, module, Sph=False, Nv=True): # MCCABE 45 self.subtitle(module, 'Cartesian') Cartesian = module.Cartesian LatLon = module.LatLon Nvector = module.Nvector if Nv else Vector4Tuple datum = Datums.Sphere if Sph else Datums.WGS84 datum2 = None if Sph else Datums.WGS72 # <https://www.Movable-Type.co.UK/scripts/geodesy/docs/ # latlon-nvector-ellipsoidal.js.html#line309> c = Cartesian(3980581, 97, 4966825, datum=datum) self.test('Cartesian0', c.toStr(prec=0), '[3980581, 97, 4966825]') self.test('Cartesian4', c.toStr(prec=4), '[3980581.0, 97.0, 4966825.0]') self.test('isEllipsoidal', c.isEllipsoidal, not Sph) self.test('isSpherical', c.isSpherical, Sph) self.testCopy(c) n = c.toNvector() # (x=0.622818, y=0.00002, z=0.782367, h=0.242887) t = n.classname # Nvector.__name__ if Nv: self.test( t, repr(n), 'Nvector(0.62538, 0.00002, 0.78032, -5918.38)' if Sph else 'Nvector(0.62282, 0.00002, 0.78237, +0.24)') self.test( t + '3', n.toStr(prec=3), '(0.625, 0.0, 0.78, -5918.38)' if Sph else '(0.623, 0.0, 0.782, +0.24)') self.test( t + '6', n.toStr(prec=6), '(0.625377, 0.000015, 0.780323, -5918.38)' if Sph else '(0.622818, 0.000015, 0.782367, +0.24)') # PYCHOK attribute else: self.test( t, repr(n), '(x=0.6253769790183048, y=1.5239375097448227e-05, z=0.7803227754472505, h=-5918.3802583276365)' if Sph else '(x=0.6228177647454303, y=1.517701139112776e-05, z=0.782366941841975, h=0.24288680875513333)', known=True) for ll in ( (50.0379, 8.5622), # FRA (51.47, 0.4543), # LHR # <https://www.EdWilliams.org/avform.htm#XTE> (degrees(0.709186), -degrees(1.287762)), # JFK (33. + 57. / 60, -(118. + 24. / 60)), # LAX # <https://GeographicLib.SourceForge.io/html/python/examples.html> (-41.32, 174.81), # WNZ, Wellington, NZ (40.96, 5.50), # SAL, Salamanca, Spain (40.1, 116.6), # BJS, Beijing Airport (37.6, -122.4)): # SFO if datum2: t = c.convertDatum(datum2).convertDatum(datum) self.test('convertDatum', t, c) # PYCHOK attribute p = LatLon(*ll) q = p.toCartesian().toLatLon() t = str(q) self.test('LatLon', t, p, known=t.endswith('m')) # PYCHOK attribute # c = Cartesian(3980581, 97, 4966825, datum=datum) t = c.copy() self.test('copy', t.isequalTo(c), True) self.test('__eq__', t == c, True) self.test('__ne__', t != c, False) if hasattr(Cartesian, 'convertRefFrame'): pass # PYCHOK attribute for B in (False, True): # check return types t = c.__class__ self.test('Cartesian', t, t) # self.testReturnType(c.Ecef, Ecef, c.Ecef.__name__) self.testReturnType(c.latlon, LatLon2Tuple, 'latlon') self.testReturnType(c.latlonheight, LatLon3Tuple, 'latlonheight') self.testReturnType(c.latlonheightdatum, LatLon4Tuple, 'latlonheightdatum') self.testReturnType(c.isequalTo(c), bool, 'isequalTo') self.testReturnType(c.philam, PhiLam2Tuple, 'philam') self.testReturnType(c.philamheight, PhiLam3Tuple, 'philamheight') self.testReturnType(c.philamheightdatum, PhiLam4Tuple, 'philamheightdatum') self.testReturnType(c.to3llh(), LatLon4Tuple, 'to3llh') self.testReturnType(c.toEcef(), Ecef9Tuple, 'toEcef') self.testReturnType(c.toLatLon(), Ecef9Tuple if B else LatLon, 'toLatLon') self.testReturnType(c.toNvector(), Vector4Tuple if B else Nvector, 'toNvector') self.testReturnType(c.xyz, Vector3Tuple, 'xyz') c = CartesianBase(c) # PYCHOK attribute if hasattr(Cartesian, 'intersections2'): # <https://GIS.StackExchange.com/questions/48937/calculating-intersection-of-two-circles> c = Cartesian(-0.00323306, -0.7915, 0.61116) self.test('intersections2', c.toLatLon(height=0), '37.673442°N, 090.234036°W') d = Cartesian(-0.0134464, -0.807775, 0.589337) self.test('intersections2', d.toLatLon(height=0), '36.109987°N, 090.95367°W') x, y = c.intersections2(0.0312705, d, 0.0421788, radius=None) # radii in radians self.test('intersections2', x.toStr(prec=6), '[-0.032779, -0.784769, 0.61892]' ) # -0.0327606, -0.784759, 0.618935 self.test('intersections2', x.toLatLon(height=0), '38.237342°N, 092.391779°W') # 38.23838°N, 092.390487°W if y is not x: self.test('intersections2', y.toStr(prec=6), '[0.025768, -0.798347, 0.601646]' ) # 0.0257661, -0.798332, 0.601666 self.test( 'intersections2', y.toLatLon(height=0), '36.987868°N, 088.151309°W') # 36.98931°N, 088.151425°W try: from pygeodesy.vector3d import intersections2 u = Vector3Tuple(-0.00323306, -0.7915, 0.61116) v = Vector3Tuple(-0.0134464, -0.807775, 0.589337) c, r = intersections2(u, 0.0312705, v, 0.0421788, sphere=True) self.test('intersections2', c.toStr(prec=6), '(-0.0035, -0.791926, 0.610589)') self.test('intersections2', r.toStr(prec=6), '0.031261', known=True) # XXX G and g formats may add 1 decimal v1, v2 = intersections2(u, 0.0312705, v, 0.0421788, sphere=False) self.test('intersections2', v1.toStr(prec=6), '(-0.021973, -0.766467, 0)') if v2 is not v1: self.test('intersections2', v2.toStr(prec=6), '(0.027459, -0.797488, 0)') except ImportError: pass
def xyz(self): '''Get the X, Y and Z components (L{Vector3Tuple}C{(x, y, z)}). ''' if self._xyz is None: self._xyz = Vector3Tuple(self.x, self.y, self.z) return self._xnamed(self._xyz)
def xyz(self): '''Get the geocentric C{(x, y, z)} coordinates (L{Vector3Tuple}C{(x, y, z)}). ''' return self._xnamed(Vector3Tuple(self.x, self.y, self.z))