def rotate(self, axis, theta): '''Rotate this vector around an axis by a specified angle. See U{Rotation matrix from axis and angle <http://WikiPedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle>} and U{Quaternion-derived rotation matrix <http://WikiPedia.org/wiki/Quaternions_and_spatial_rotation#Quaternion-derived_rotation_matrix>}. @param axis: The axis being rotated around (L{Vector3d}). @param theta: The angle of rotation (C{radians}). @return: New, rotated vector (L{Vector3d}). @JSname: I{rotateAround}. ''' 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.classof( 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 toOsgr(latlon, lon=None, datum=Datums.WGS84, Osgr=Osgr): '''Convert a lat-/longitude point to an OSGR coordinate. @param latlon: Latitude (degrees) or an (ellipsoidal) geodetic I{LatLon} point. @keyword lon: Optional longitude in degrees (scalar or None). @keyword datum: Optional datum to convert (I{Datum}). @keyword Osgr: Optional Osgr class to use for the OSGR coordinate (L{Osgr}). @return: The OSGR coordinate (L{Osgr}). @raise TypeError: If I{latlon} is not ellipsoidal or if I{datum} conversion failed. @raise ValueError: Invalid I{latlon} or I{lon}. @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 not isinstance(latlon, _eLLb): # XXX fix failing _eLLb.convertDatum() latlon = _eLLb(*parseDMS2(latlon, lon), datum=datum) elif lon is not None: raise ValueError('%s not %s: %r' % ('lon', None, lon)) E = _OSGB36.ellipsoid ll = _ll2datum(latlon, _OSGB36, 'latlon') a, b = map1(radians, ll.lat, ll.lon) ca, sa, ta = cos(a), sin(a), tan(a) s = E.e2s2(sa) v = E.a * _F0 / sqrt(s) # nu r = s / E.e12 # nu / rho == v / (v * E.e12 / s) x2 = r - 1 # η2 ca3, ca5 = fpowers(ca, 5, 3) # PYCHOK false! ta2, ta4 = fpowers(ta, 4, 2) # PYCHOK false! vsa = v * sa I4 = (E.b * _F0 * _M(E.Mabcd, a) + _N0, (vsa / 2) * ca, (vsa / 24) * ca3 * fsum_(5, -ta2, 9 * x2), (vsa / 720) * ca5 * fsum_(61, ta4, -58 * ta2)) V4 = (_E0, (v * ca), (v / 6) * ca3 * (r - ta2), (v / 120) * ca5 * fdot( (-18, 1, 14, -58), ta2, 5 + ta4, x2, ta2 * x2)) d, d2, d3, d4, d5, d6 = fpowers(b - _B0, 6) # PYCHOK false! n = fdot(I4, 1, d2, d4, d6) e = fdot(V4, 1, d, d3, d5) return Osgr(e, n)
def _rsT(T): '''(INTERNAL) Helper for C{_RD}, C{_RF} and C{_RJ}. ''' s = map2(sqrt, T[1:]) r = fdot(s[:3], s[1], s[2], s[0]) T[:] = [(t + r) * 0.25 for t in T] return r, s, T
def _M(Mabcd, a): '''(INTERNAL) Compute meridional arc. ''' a_ = a - _A0 _a = a + _A0 return fdot(Mabcd, a_, -sin(a_) * cos(_a), sin(a_ * 2) * cos(_a * 2), -sin(a_ * 3) * cos(_a * 3))
def _hIDW(self, x, y): # interpolate height at (x, y) radians ws = tuple(self._distances(x, y)) w, h = min(zip(ws, self._hs)) if w > EPS: ws = tuple(w**self._b for w in ws) h = fdot(ws, *self._hs) / fsum(ws) return h
def dot(self, other): '''Compute the dot (scalar) product of this and an other vector. @param other: The other vector (L{Vector3d}). @return: Dot product (C{float}). @raise TypeError: Incompatible I{other} C{type}. ''' self.others(other) return fdot(self.to3xyz(), *other.to3xyz())
def destinationNed(self, delta): '''Calculate the destination point using the supplied NED 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: If {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 v.toLatLon( datum=self.datum, LatLon=self.classof) # Cartesian(v.x, v.y, v.z).toLatLon(...)
def isconvex_(points, adjust=False, wrap=True): '''Determine whether a polygon is convex and clockwise. @param points: The polygon points (C{LatLon}[]). @keyword adjust: Adjust the wrapped, unrolled longitudinal delta by the cosine of the mean latitude (C{bool}). @keyword wrap: Wrap lat-, wrap and unroll longitudes (C{bool}). @return: C{+1} if I{points} are convex clockwise, C{-1} for convex counter-clockwise I{points}, C{0} otherwise. @raise CrossError: Some I{points} are colinear. @raise TypeError: Some I{points} are not C{LatLon}. @raise ValueError: Insufficient number of I{points}. @example: >>> t = LatLon(45,1), LatLon(46,1), LatLon(46,2) >>> isconvex_(t) # +1 >>> f = LatLon(45,1), LatLon(46,2), LatLon(45,2), LatLon(46,1) >>> isconvex_(f) # 0 ''' def _unroll_adjust(x1, y1, x2, y2, wrap): x21, x2 = unroll180(x1, x2, wrap=wrap) if adjust: x21 *= cos(radians(y1 + y2) * 0.5) return x21, x2 pts = LatLon2psxy(points, closed=True, radius=None, wrap=wrap) c, n, s = crosserrors(), len(pts), 0 x1, y1, _ = pts[n - 2] x2, y2, _ = pts[n - 1] x21, x2 = _unroll_adjust(x1, y1, x2, y2, False) for i in range(n): x3, y3, ll = pts[i] x32, x3 = _unroll_adjust(x2, y2, x3, y3, wrap if i < (n - 2) else False) # get the sign of the distance from point # x3, y3 to the line from x1, y1 to x2, y2 # <http://WikiPedia.org/wiki/Distance_from_a_point_to_a_line> s3 = fdot((x3, y3, x1, y1), y2 - y1, -x21, -y2, x2) if s3 > 0: # x3, y3 on the right if s < 0: # non-convex return 0 s = +1 elif s3 < 0: # x3, y3 on the left if s > 0: # non-convex return 0 s = -1 elif c and fdot((x32, y1 - y2), y3 - y2, -x21) < 0: # colinear u-turn: x3, y3 not on the # opposite side of x2, y2 as x1, y1 raise CrossError('%s %s: %r' % ('colinear', 'point', ll)) x1, y1, x2, y2, x21 = x2, y2, x3, y3, x32 return s # all points on the same side
def toLatLon(self, LatLon=None, datum=Datums.WGS84): '''Convert this OSGR coordinate to an (ellipsoidal) geodetic point. I{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.} @keyword LatLon: Optional ellipsoidal LatLon class to use for the point (I{LatLon}). @keyword datum: Optional datum to use (I{Datum}). @return: The geodetic point (I{LatLon}) or 3-tuple (lat, lon, datum) if I{LatLon} is None. @raise TypeError: If I{LatLon} is not ellipsoidal or if I{datum} conversion failed. @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: return self._latlon3(LatLon, datum) E = _OSGB36.ellipsoid # Airy130 a_F0 = E.a * _F0 b_F0 = E.b * _F0 e, n = self._easting, self._northing n_N0 = n - _N0 a, M = _A0, 0 sa = Fsum(a) while True: t = n_N0 - M if t < _10um: break sa.fadd(t / a_F0) a = sa.fsum() M = b_F0 * _M(E.Mabcd, a) ca, sa, ta = cos(a), sin(a), tan(a) s = E.e2s2(sa) v = a_F0 / sqrt(s) # nu r = v * E.e12 / s # rho vr = v / r # == s / E.e12 x2 = vr - 1 # η2 v3, v5, v7 = fpowers(v, 7, 3) # PYCHOK false! ta2, ta4, ta6 = fpowers(ta**2, 3) # PYCHOK false! tar = ta / r V4 = (a, tar / (2 * v), tar / (24 * v3) * fdot( (1, 3, -9), 5 + x2, ta2, ta2 * x2), tar / (720 * v5) * fdot( (61, 90, 45), 1, ta2, ta4)) csa = 1.0 / ca X5 = (_B0, csa / v, csa / (6 * v3) * fsum_(vr, ta, ta), csa / (120 * v5) * fdot( (5, 28, 24), 1, ta2, ta4), csa / (5040 * v7) * fdot( (61, 662, 1320, 720), ta, ta2, ta4, ta6)) d, d2, d3, d4, d5, d6, d7 = fpowers(e - _E0, 7) # PYCHOK false! a = fdot(V4, 1, -d2, d4, -d6) b = fdot(X5, 1, d, -d3, d5, -d7) self._latlon = _eLLb(degrees90(a), degrees180(b), datum=_OSGB36) return self._latlon3(LatLon, datum)
def toOsgr(latlon, lon=None, datum=Datums.WGS84, Osgr=Osgr, name=''): '''Convert a lat-/longitude point to an OSGR coordinate. @param latlon: Latitude (C{degrees}) or an (ellipsoidal) geodetic C{LatLon} point. @keyword lon: Optional longitude in degrees (scalar or C{None}). @keyword datum: Optional datum to convert (C{Datum}). @keyword Osgr: Optional (sub-)class to return the OSGR coordinate (L{Osgr}) or C{None}. @keyword name: Optional I{Osgr} name (C{str}). @return: The OSGR coordinate (L{Osgr}) or 2-tuple (easting, northing) if I{Osgr} is C{None}. @raise TypeError: Non-ellipsoidal I{latlon} or I{datum} conversion failed. @raise ValueError: Invalid I{latlon} or I{lon}. @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 not isinstance(latlon, _LLEB): # XXX fix failing _LLEB.convertDatum() latlon = _LLEB(*parseDMS2(latlon, lon), datum=datum) elif lon is not None: raise ValueError('%s not %s: %r' % ('lon', None, lon)) elif not name: # use latlon.name name = _nameof(latlon) or name # PYCHOK no effect E = _OSGB36.ellipsoid ll = _ll2datum(latlon, _OSGB36, 'latlon') a, b = map1(radians, ll.lat, ll.lon) sa, ca = sincos2(a) s = E.e2s2(sa) v = E.a * _F0 / sqrt(s) # nu r = s / E.e12 # nu / rho == v / (v * E.e12 / s) x2 = r - 1 # η2 ta = tan(a) ca3, ca5 = fpowers(ca, 5, 3) # PYCHOK false! ta2, ta4 = fpowers(ta, 4, 2) # PYCHOK false! vsa = v * sa I4 = (E.b * _F0 * _M(E.Mabcd, a) + _N0, (vsa / 2) * ca, (vsa / 24) * ca3 * fsum_(5, -ta2, 9 * x2), (vsa / 720) * ca5 * fsum_(61, ta4, -58 * ta2)) V4 = (_E0, (v * ca), (v / 6) * ca3 * (r - ta2), (v / 120) * ca5 * fdot( (-18, 1, 14, -58), ta2, 5 + ta4, x2, ta2 * x2)) d, d2, d3, d4, d5, d6 = fpowers(b - _B0, 6) # PYCHOK false! n = fdot(I4, 1, d2, d4, d6) e = fdot(V4, 1, d, d3, d5) return (e, n) if Osgr is None else _xnamed(Osgr(e, n), name)
def _xdot(d, a1, b1, a, b, wrap): # compute dot product d . (-b + b1, a - a1) db, _ = unrollPI(b1, radians(b), wrap=wrap) return fdot(d, db, radians(a) - a1)
def isconvex(points, adjust=False, wrap=True): '''Determine whether a polygon defined by an array, list, sequence, set or tuple of points is convex. @param points: The points defining the polygon (I{LatLon}[]). @keyword adjust: Adjust the wrapped, unrolled longitudinal delta by the cosine of the mean latitude (bool). @keyword wrap: Wrap lat-, wrap and unroll longitudes (bool). @return: True if convex, False otherwise. @raise CrossError: Colinear point. @raise TypeError: Some I{points} are not I{LatLon}. @raise ValueError: Insufficient number of I{points}. @example: >>> t = LatLon(45,1), LatLon(46,1), LatLon(46,2) >>> isconvex(t) # True >>> f = LatLon(45,1), LatLon(46,2), LatLon(45,2), LatLon(46,1) >>> isconvex(f) # False ''' def _unroll_adjust(x1, y1, x2, y2): x21, x2 = unroll180(x1, x2, wrap=wrap) if adjust: x21 *= cos(radians(y1 + y2) * 0.5) return x21, x2 pts = LatLon2psxy(points, closed=True, radius=None, wrap=wrap) c, n, s = crosserrors(), len(pts), None x1, y1, _ = pts[n - 2] x2, y2, _ = pts[n - 1] x21, x2 = _unroll_adjust(x1, y1, x2, y2) for i in range(n): x3, y3, ll = pts[i] x32, x3 = _unroll_adjust(x2, y2, x3, y3) # get the sign of the distance from point # x3, y3 to the line from x1, y1 to x2, y2 # <http://wikipedia.org/wiki/Distance_from_a_point_to_a_line> s3 = fdot((x3, y3, x1, y1), y2 - y1, -x21, -y2, x2) if s3 > 0: # x3, y3 on the left if s is None: s = True elif not s: # different side return False elif s3 < 0: # x3, y3 on the right if s is None: s = False elif s: # different side return False elif c and fdot((x32, y1 - y2), y3 - y2, -x21) < 0: # colinear u-turn: x3, y3 not on the # opposite side of x2, y2 as x1, y1 raise CrossError('%s %s: %r' % ('colinear', 'point', ll)) x1, y1, x2, y2, x21 = x2, y2, x3, y3, x32 return True # all points on the same side