def destinationNed(self, delta): '''Calculates destination point using supplied delta from this point. @param delta: Delta from this to the other point in the local tangent plane (LTP) of this point (L{Ned}). @return: Destination point (L{Cartesian}). @raise TypeError: The delta is not L{Ned}. @example: >>> a = LatLon(49.66618, 3.45063) >>> delta = toNed(116807.681, 222.493, -0.5245) # [N:-86126, E:-78900, D:1069] >>> b = a.destinationNed(delta) # 48.88667°N, 002.37472°E @JSname: I{destinationPoint}. ''' if not isinstance(delta, Ned): raise TypeError('type(%s) not %s.%s' % ('delta', Ned.__module__, Ned.__name__)) n, e, d = self._rotation3() # convert NED delta to standard Vector3d in coordinate frame of n-vector dn = delta.toVector3d().to3xyz() # rotate dn to get delta in cartesian (ECEF) coordinate # reference frame using the rotation matrix column vectors dc = Cartesian(fdot(dn, n.x, e.x, d.x), fdot(dn, n.y, e.y, d.y), fdot(dn, n.z, e.z, d.z)) # apply (cartesian) delta to this Cartesian to # obtain destination point as cartesian v = self.toCartesian().plus(dc) # the plus() gives a plain vector return Cartesian(v.x, v.y, v.z).toLatLon(datum=self.datum)
def Mabcd(self): '''Get the OSGR meridional coefficients, Airy130 only (4-tuple). ''' if self._Mabcd is None: n = self.n n2 = n * n n3 = n * n2 # XXX i/i quotients require from __future__ import division self._Mabcd = (fdot((1, n, n2, n3), 1, 1, 5 / 4, 5 / 4), fdot((n, n2, n3), 3, 3, 21 / 8), fdot((n2, n3), 15 / 8, 15 / 8), 35 / 24 * n3) return self._Mabcd
def _M(Mabcd, a): '''(INTERNAL) Compute meridional arc. ''' a_ = a - _A0 _a = a + _A0 return _F0 * fdot(Mabcd, a_, -sin(a_) * cos(_a), sin(a_ * 2) * cos(_a * 2), -sin(a_ * 3) * cos(_a * 3))
def dot(self, other): '''Dot (scalar) product of this and an other vector. @param other: The other vector (L{Vector3d}). @return: Dot product (float). ''' self.others(other) return fdot(self.to3xyz(), *other.to3xyz())
def transform(self, x, y, z, inverse=False): '''Transform a (geocentric) Cartesian point, forward or inverse. @param x: X coordinate (meter). @param y: Y coordinate (meter). @param z: Z coordinate (meter). @keyword inverse: Direction, forward or inverse (bool). @return: 3-Tuple (x, y, z) transformed. ''' if inverse: xyz = -1, -x, -y, -z _s1 = self.s1 - 2 # negative inverse: -(1 - s * 1.e-6) 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) return (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))
def rotate(self, axis, theta): '''Rotate this vector by a specified angle around an axis. See U{Rotation matrix from axis and angle<http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle/Quaternions_and_spatial_rotation#Quaternion-derived_rotation_matrix/Rodrigues'_rotation_formula...>} @param axis: The axis being rotated around (L{Vector3d}). @param theta: The angle of rotation (radians). @return: New, rotated vector (L{Vector3d}). ''' self.others(axis, name='axis') c = cos(theta) a = axis.unit() # axis being rotated around b = a.times(1 - c) s = a.times(sin(theta)) p = self.unit().to3xyz() # point being rotated # multiply p by a quaternion-derived rotation matrix return self.topsub( fdot(p, a.x * b.x + c, a.x * b.y - s.z, a.x * b.z + s.y), fdot(p, a.y * b.x + s.z, a.y * b.y + c, a.y * b.z - s.x), fdot(p, a.z * b.x - s.y, a.z * b.y + s.x, a.z * b.z + c))
def _K6(self, *fs6): '''(INTERNAL) Compute 6th-order Krüger Alpha or Beta series per Karney 2011, 'Transverse Mercator with an accuracy of a few nanometers', page 7, equations 35 and 36, see <https://arxiv.org/pdf/1002.1417v3.pdf>. @param fs6: 6-Tuple of coefficent tuples. @return: 6th-Order Krüger (7-tuple, 1-origin). ''' ns = [self.n] # 3rd flattening: n, n**2, ... n**6 for i in range(len(fs6) - 1): ns.append(ns[0] * ns[i]) k6 = [0] # 1-origin for fs in fs6: i = len(ns) - len(fs) k6.append(fdot(fs, *ns[i:])) return tuple(k6)
def toOsgr(latlon, lon=None, datum=Datums.WGS84): '''Convert lat-/longitude to a OSGR coordinate. @param latlon: Latitude in degrees (scalar) or an ellipsoidal LatLon location. @keyword lon: Longitude in degrees (scalar or None). @keyword datum: Datum to use (L{Datum}). @return: The OSGR coordinate (L{Osgr}). @raise TypeError: If latlon is not ellipsoidal. @raise ValueError: If lon is invalid, not None. @example: >>> p = LatLon(52.65798, 1.71605) >>> r = toOsgr(p) # TG 51409 13177 >>> # for conversion of (historical) OSGB36 lat-/longitude: >>> r = toOsgr(52.65757, 1.71791, datum=Datums.OSGB36) ''' if isscalar(latlon) and isscalar(lon): # XXX any ellipsoidal LatLon with .convertDatum latlon = LatLonEllipsoidalBase(latlon, lon, datum=datum) elif not hasattr(latlon, 'convertDatum'): raise TypeError('%s not ellipsoidal: %r' % ('latlon', latlon)) elif lon is not None: raise ValueError('%s not %s: %r' % ('lon', None, lon)) if latlon.datum != _OSGB36: latlon = latlon.convertDatum(_OSGB36) E = _OSGB36.ellipsoid a, b = radians(latlon.lat), radians(latlon.lon) ca, sa, ta = cos(a), sin(a), tan(a) s = 1 - E.e2 * sa * sa v = E.a * _F0 / sqrt(s) r = s / E.e12 # = v / r = v / (v * E.e12 / s) ca3 = ca * ca * ca ca5 = ca * ca * ca3 ta2 = ta * ta ta4 = ta2 * ta2 x2 = r - 1 # η I4 = (E.b * _M(E.Mabcd, a) + _N0, (v / 2) * sa * ca, (v / 24) * sa * ca3 * (5 - ta2 + 9 * x2), (v / 720) * sa * ca5 * (61 - 58 * ta2 + ta4)) V4 = (_E0, v * ca, (v / 6) * ca3 * (r - ta2), (v / 120) * ca5 * (5 - 18 * ta2 + ta4 + 14 * x2 - 58 * ta2 * x2)) d = b - _B0 d2 = d * d d3 = d2 * d d5 = d2 * d3 n = fdot(I4, 1, d2, d3 * d, d5 * d) e = fdot(V4, 1, d, d3, d5) return Osgr(e, n)
def toLatLon(self, LatLon, datum=Datums.WGS84): '''Convert this OSGR coordinate to an ellipsoidal lat-/longitude point. Note formulation implemented here due to Thomas, Redfearn, etc. is as published by OS, but is inferior to Krüger as used by e.g. Karney 2011. @param LatLon: Ellipsoidal LatLon class to use (L{LatLon}). @param datum: Darum to use (L{Datum}). @return: The elliposoidal point (L{LatLon}). @raise TypeError: If L{LatLon} is not ellipsoidal. @example: >>> from pygeodesy import ellipsoidalVincenty as eV >>> g = Osgr(651409.903, 313177.270) >>> p = g.toLatLon(ev.LatLon) # 52°39′28.723″N, 001°42′57.787″E >>> # to obtain (historical) OSGB36 lat-/longitude point >>> p = g.toLatLon(ev.LatLon, datum=Datums.OSGB36) # 52°39′27.253″N, 001°43′04.518″E ''' if self._latlon and self._latlon.__class__ is LatLon \ and self._latlon.datum == datum: return self._latlon # set below if not issubclass(LatLon, LatLonEllipsoidalBase): raise TypeError('%s not %s: %r' % ('LatLon', 'ellipsoidal', LatLon)) E = _OSGB36.ellipsoid # Airy130 Mabcd = E.Mabcd e, n = self._easting, self._northing a, M = _A0, 0 while True: t = n - _N0 - M if t < _10um: break a += t / (E.a * _F0) M = E.b * _M(Mabcd, a) ca, sa, ta = cos(a), sin(a), tan(a) s = 1 - E.e2 * sa * sa v = E.a * _F0 / sqrt(s) r = v * E.e12 / s x2 = v / r - 1 # η v3 = v * v * v v5 = v * v * v3 v7 = v * v * v5 ta2 = ta * ta ta4 = ta2 * ta2 ta6 = ta4 * ta2 V4 = (a, ta / ( 2 * r * v), ta / ( 24 * r * v3) * fdot((5, 3, 1, -9), 1, ta2, x2, x2 * ta2), ta / (720 * r * v5) * fdot((61, 90, 45), 1, ta2, ta4)) sca = 1 / ca X5 = (_B0, sca / v, sca / ( 6 * v3) * (v / r + 2 * ta), sca / ( 120 * v5) * fdot((5, 28, 24), 1, ta2, ta4), sca / (5040 * v7) * fdot((61, 662, 1320, 720), ta, ta2, ta4, ta6)) d = e - _E0 d2 = d * d d4 = d2 * d2 d6 = d2 * d4 a = fdot(V4, 1, -d2, d4, -d6) b = fdot(X5, 1, d, -d2 * d, d4 * d, -d6 * d) ll = LatLon(degrees90(a), degrees180(b), datum=_OSGB36) if datum != _OSGB36: ll = ll.convertDatum(datum) ll = LatLon(ll.lat, ll.lon, datum=datum) self._latlon = ll return ll