def forward(self, latlonh, lon=None, height=0, M=False): '''Convert from geodetic C{(lat, lon, height)} to geocentric C{(x, y, z)}. @param latlonh: Either a C{LatLon}, an L{Ecef9Tuple} or C{scalar} latitude in C{degrees}. @keyword lon: Optional C{scalar} longitude in C{degrees} for C{scalar} B{C{latlonh}}. @keyword height: Optional height in C{meter}, vertically above (or below) the surface of the ellipsoid. @keyword M: Optionally, return the rotation L{EcefMatrix} (C{bool}). @return: An L{Ecef9Tuple}C{(x, y, z, lat, lon, height, C, M, datum)} with geocentric C{(x, y, z)} coordinates for the given geodetic ones C{(lat, lon, height)}, case C{C} 0, L{EcefMatrix} C{M} and C{datum} if available. @raise EcefError: If B{C{latlonh}} not C{LatLon}, L{Ecef9Tuple} or C{scalar} or B{C{lon}} not C{scalar} for C{scalar} B{C{latlonh}} or C{abs(B{lat})} exceeds 90°. ''' lat, lon, h, name = _llhn4(latlonh, lon, height, '') sa, ca, sb, cb = sincos2d(lat, lon) E = self.ellipsoid n = 1.0 / hypot(E.a * ca, E.b * sa) z = (n * E.b2 + h) * sa x = (n * E.a2 + h) * ca m = self._Matrix(sa, ca, sb, cb) if M else None r = Ecef9Tuple(x * cb, x * sb, z, lat, lon, h, 0, m, self.datum) return self._xnamed(r, name)
def destination(self, distance, bearing, radius=R_M, height=None): '''Locate the destination from this point after having travelled the given distance on the given bearing. @param distance: Distance travelled (C{meter}, same units as B{C{radius}}). @param bearing: Bearing from this point (compass C{degrees360}). @keyword radius: Mean earth radius (C{meter}). @keyword height: Optional height at destination, overriding the default height (C{meter}, same units as B{C{radius}}). @return: Destination point (L{LatLon}). @raise Valuerror: Polar coincidence. @example: >>> p = LatLon(51.4778, -0.0015) >>> q = p.destination(7794, 300.7) >>> q.toStr() # 51.513546°N, 000.098345°W @JSname: I{destinationPoint}. ''' p = self.toNvector() e = NorthPole.cross(p, raiser='pole').unit() # east vector at p n = p.cross(e) # north vector at p s, c = sincos2d(bearing) q = n.times(c).plus(e.times(s)) # direction vector @ p s, c = sincos2(float(distance) / float(radius)) # angular distance in radians n = p.times(c).plus(q.times(s)) return n.toLatLon(height=height, LatLon=self.classof) # Nvector(n.x, n.y, n.z).toLatLon(...)
def greatCircle(self, bearing): '''Compute the n-vector normal to great circle obtained by heading on given compass bearing from this point as its n-vector. Direction of vector is such that initial bearing vector b = c × p. @param bearing: Initial compass bearing (C{degrees}). @return: N-vector representing great circle (L{Nvector}). @raise Valuerror: Polar coincidence. @example: >>> n = LatLon(53.3206, -1.7297).toNvector() >>> gc = n.greatCircle(96.0) # [-0.794, 0.129, 0.594] ''' s, c = sincos2d(bearing) e = NorthPole.cross(self, raiser='pole') # easting n = self.cross(e, raiser='point') # northing e = e.times(c / e.length) n = n.times(s / n.length) return n.minus(e)
def toNed(distance, bearing, elevation, Ned=Ned, name=NN): '''Create an NED vector from distance, bearing and elevation (in local coordinate system). @arg distance: NED vector length (C{meter}). @arg bearing: NED vector bearing (compass C{degrees360}). @arg elevation: NED vector elevation from local coordinate frame horizontal (C{degrees}). @kwarg Ned: Optional class to return the NED (L{Ned}) or C{None}. @kwarg name: Optional name (C{str}). @return: An NED vector equivalent to this B{C{distance}}, B{C{bearing}} and B{C{elevation}} (L{Ned}) or if B{C{Ned=None}}, an L{Ned3Tuple}C{(north, east, down)}. @raise ValueError: Invalid B{C{distance}}, B{C{bearing}} or B{C{elevation}}. @JSname: I{fromDistanceBearingElevation}. ''' d = Distance(distance) sb, cb, se, ce = sincos2d(Bearing(bearing), Height(elevation, name=_elevation_)) n = cb * d * ce e = sb * d * ce d *= se r = Ned3Tuple(n, e, -d) if Ned is None else \ Ned(n, e, -d) return _xnamed(r, name)
def _gc(p, b): n = p.toNvector() de = NorthPole.cross(n, raiser='pole').unit() # east vector @ n dn = n.cross(de) # north vector @ n s, c = sincos2d(b) dest = de.times(s) dnct = dn.times(c) d = dnct.plus(dest) # direction vector @ n return n.cross(d) # great circle point + bearing
def forward(self, lat, lon, lon0=0, name=NN): '''Convert a geodetic location to east- and northing. @arg lat: Latitude of the location (C{degrees}). @arg lon: Longitude of the location (C{degrees}). @kwarg lon0: Optional central meridian longitude (C{degrees}). @kwarg name: Optional name for the location (C{str}). @return: An L{Albers7Tuple}C{(x, y, lat, lon, gamma, scale, datum)}. @note: The origin latitude is returned by C{property lat0}. No false easting or northing is added. The value of B{C{lat}} should be in the range C{[-90..90] degrees}. The returned values C{x} and C{y} will be large but finite for points projecting to infinity, i.e. one or both of the poles. ''' E = self.datum.ellipsoid s = self._sign k0 = self._k0 n0 = self._n0 nrho0 = self._nrho0 txi0 = self._txi0 sa, ca = sincos2d(_Lat_(lat) * s) ca = max(_EPSX, ca) ta = sa / ca _, sxi, txi = self._cstxif3(ta) dq = self._qZ * _Dsn(txi, txi0, sxi, self._sxi0) * (txi - txi0) drho = -E.a * dq / (sqrt(self._m02 - n0 * dq) + self._m0) lon = _Lon_(lon) if lon0: lon, _ = _diff182(_Lon_(lon0, name=_lon0_), lon) b = radians(lon) th = self._k02n0 * b sth, cth = sincos2(th) # XXX sin, cos if n0: x = sth / n0 y = (1 - cth if cth < 0 else sth**2 / (1 + cth)) / n0 else: x = self._k02 * b y = 0 t = nrho0 + n0 * drho x = t * x / k0 y = s * (nrho0 * y - drho * cth) / k0 g = degrees360(s * th) if t: k0 *= t * hypot1(E.b_a * ta) / E.a t = Albers7Tuple(x, y, lat, lon, g, k0, self.datum) return _xnamed(t, name or self.name)
def fEd(self, deg): '''The incomplete integral of the second kind with the argument given in degrees. @param deg: Angle (C{degrees}). @return: E(π deg/180, k). ''' n = ceil(deg / 360.0 - 0.5) sn, cn = sincos2d(deg - n * 360.0) return self.fE(sn, cn, self.fDelta(sn, cn)) + 4 * self.cE * n
def reset(self, lat0, lon0): '''Set or reset the center point of this azimuthal projection. @arg lat0: Center point latitude (C{degrees90}). @arg lon0: Center point longitude (C{degrees180}). @raise AzimuthalError: Invalid B{C{lat0}} or B{C{lon0}}. ''' self._latlon0 = LatLon2Tuple(Lat_(lat0=lat0, Error=AzimuthalError), Lon_(lon0=lon0, Error=AzimuthalError)) self._sc0 = tuple(sincos2d(self.lat0)) self._radius = None
def fEd(self, deg): '''The incomplete integral of the second kind with the argument given in degrees. @arg deg: Angle (C{degrees}). @return: E(π B{C{deg}}/180, k) (C{float}). @raise EllipticError: No convergence. ''' n = ceil(deg / 360.0 - 0.5) sn, cn = sincos2d(deg - n * 360.0) return self.fE(sn, cn, self.fDelta(sn, cn)) + 4 * self.cE * n
def __init__(self, lat, k0=1, datum=Datums.WGS84, name=NN): '''New L{AlbersEqualArea} projection. @arg lat: Standard parallel (C{degrees}). @kwarg k0: Azimuthal scale on the standard parallel (C{scalar}). @kwarg datum: Optional datum or ellipsoid (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @kwarg name: Optional name for the projection (C{str}). @raise AlbertError: Invalid B{C{lat}}, B{C{k0}} or no convergence. ''' self._lat1 = self._lat2 = lat = _Lat(lat1=lat) args = tuple(sincos2d(lat)) * 2 + (_Ks(k0=k0), datum, name) _AlbersBase.__init__(self, *args)
def __init__(self, lat1, lat2, k1=1, datum=Datums.WGS84, name=NN): '''New L{AlbersEqualArea2} projection. @arg lat1: First standard parallel (C{degrees}). @arg lat2: Second standard parallel (C{degrees}). @kwarg k1: Azimuthal scale on the standard parallels (C{scalar}). @kwarg datum: Optional datum or ellipsoid (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @kwarg name: Optional name for the projection (C{str}). @raise AlbertError: Invalid B{C{lat1}}m B{C{lat2}}, B{C{k1}} or no convergence. ''' self._lat1, self._lat2 = lats = _Lat(lat1=lat1), _Lat(lat2=lat2) args = tuple(sincos2d(*lats)) + (_Ks(k1=k1), datum, name) _AlbersBase.__init__(self, *args)
def _forward(self, lat, lon, name, _k_t): '''(INTERNAL) Azimuthal (spherical) forward C{lat, lon} to C{x, y}. ''' sa, ca, sb, cb = sincos2d(Lat_(lat), Lon_(lon) - self.lon0) s0, c0 = self._sc0 k, t = _k_t(s0 * sa + c0 * ca * cb) if t: r = k * self.radius x = r * ca * sb y = r * (c0 * sa - s0 * ca * cb) z = atan2b(x, y) # (x, y) for azimuth from true North else: # 0 or 180 x = y = z = 0 t = Azimuthal7Tuple(x, y, lat, lon, z, k, self.datum) return self._xnamed(t, name=name)
def rhumbDestination(self, distance, bearing, radius=R_M, height=None): '''Return the destination point having travelled along a rhumb (loxodrome) line from this point the given distance on the given bearing. @param distance: Distance travelled (C{meter}, same units as I{radius}). @param bearing: Bearing from this point (compass C{degrees360}). @keyword radius: Optional, mean earth radius (C{meter}). @keyword height: Optional height, overriding the default height (C{meter}, same unit as I{radius}). @return: The destination point (spherical C{LatLon}). @example: >>> p = LatLon(51.127, 1.338) >>> q = p.rhumbDestination(40300, 116.7) # 50.9642°N, 001.8530°E @JSname: I{rhumbDestinationPoint} ''' a1, b1 = self.to2ab() r = float(distance) / float(radius) # angular distance in radians sb, cb = sincos2d(bearing) da = r * cb a2 = a1 + da # normalize latitude if past pole if a2 > PI_2: a2 = PI - a2 elif a2 < -PI_2: a2 = -PI - a2 dp = log(tanPI_2_2(a2) / tanPI_2_2(a1)) # E-W course becomes ill-conditioned with 0/0 q = (da / dp) if abs(dp) > EPS else cos(a1) b2 = (b1 + r * sb / q) if abs(q) > EPS else b1 h = self.height if height is None else height return self.classof(degrees90(a2), degrees180(b2), height=h)
def forward(self, latlonh, lon=None, height=0, M=False): '''Convert from geodetic C{(lat, lon, height)} to geocentric C{(x, y, z)}. @arg latlonh: Either a C{LatLon}, an L{Ecef9Tuple} or C{scalar} latitude in C{degrees}. @kwarg lon: Optional C{scalar} longitude in C{degrees} for C{scalar} B{C{latlonh}}. @kwarg height: Optional height in C{meter}, vertically above (or below) the surface of the ellipsoid. @kwarg M: Optionally, return the rotation L{EcefMatrix} (C{bool}). @return: An L{Ecef9Tuple}C{(x, y, z, lat, lon, height, C, M, datum)} with geocentric C{(x, y, z)} coordinates for the given geodetic ones C{(lat, lon, height)}, case C{C} 0, optional L{EcefMatrix} C{M} and C{datum} if available. @raise EcefError: If B{C{latlonh}} not C{LatLon}, L{Ecef9Tuple} or C{scalar} or B{C{lon}} not C{scalar} for C{scalar} B{C{latlonh}} or C{abs(lat)} exceeds 90°. @note: Let C{v} be a unit vector located at C{(lat, lon, h)}. We can express C{v} as column vectors in one of two ways, C{v1} in east, north, up coordinates (where the components are relative to a local coordinate system at C{C(lat0, lon0, h0)}) or as C{v0} in geocentric C{x, y, z} coordinates. Then, M{v0 = M ⋅ v1} where C{M} is the rotation matrix. ''' lat, lon, h, name = _llhn4(latlonh, lon, height, '') sa, ca, sb, cb = sincos2d(lat, lon) n = self.a / self.ellipsoid.e2s(sa) # ... / sqrt(1 - self.e2 * sa**2) z = (self.e2m * n + h) * sa x = (n + h) * ca m = self._Matrix(sa, ca, sb, cb) if M else None r = Ecef9Tuple(x * cb, x * sb, z, lat, lon, h, 0, m, self.datum) return self._xnamed(r, name)
def forward(self, lat, lon, name=NN, raiser=True): '''Convert an (ellipsoidal) geodetic location to azimuthal gnomonic east- and northing. @arg lat: Latitude of the location (C{degrees90}). @arg lon: Longitude of the location (C{degrees180}). @kwarg name: Optional name for the location (C{str}). @kwarg raiser: Do or don't throw an error (C{bool}) if the location lies over the horizon. @return: An L{Azimuthal7Tuple}C{(x, y, lat, lon, azimuth, scale, datum)} with C{x} and C{y} in C{meter} and C{lat} and C{lon} in C{degrees} and C{azimuth} clockwise from true North. The C{scale} of the projection is C{1 / reciprocal**2} in I{radial} direction and C{1 / reciprocal} in the direction perpendicular to this. Both C{x} and C{y} will be C{NAN} if the geodetic location lies over the horizon and B{C{raiser}} is C{False}. @raise AzimuthalError: Invalid B{C{lat}}, B{C{lon}} or the geodetic location lies over the horizon and B{C{raiser}} is C{True}. ''' self._iteration = 0 r = self.geodesic.Inverse(self.lat0, self.lon0, Lat_(lat), Lon_(lon), self._mask) if r.M21 < EPS: if raiser: raise AzimuthalError(lat=lat, lon=lon, txt=_over_horizon_) x = y = NAN else: q = r.m12 / r.M21 # .M12 x, y = sincos2d(r.azi1) x *= q y *= q t = self._7Tuple(x, y, r, r.M21) t._iteraton = self._iteration # = 0 return self._xnamed(t, name=name)
def forward(self, lat, lon, name=NN): '''Convert an (ellipsoidal) geodetic location to azimuthal equidistant east- and northing. @arg lat: Latitude of the location (C{degrees90}). @arg lon: Longitude of the location (C{degrees180}). @kwarg name: Optional name for the location (C{str}). @return: An L{Azimuthal7Tuple}C{(x, y, lat, lon, azimuth, scale, datum)} with C{x} and C{y} in C{meter} and C{lat} and C{lon} in C{degrees}. The C{scale} of the projection is C{1} in I{radial} direction, C{azimuth} clockwise from true North and is C{1 / reciprocal} in the direction perpendicular to this. @see: Method L{EquidistantKarney.reverse}. A call to C{.forward} followed by a call to C{.reverse} will return the original C{lat, lon} to within roundoff. @raise AzimuthalError: Invalid B{C{lat}} or B{C{lon}}. ''' r = self.geodesic.Inverse(self.lat0, self.lon0, Lat_(lat), Lon_(lon), self._mask) x, y = sincos2d(r.azi1) t = self._7Tuple(x * r.s12, y * r.s12, r) return self._xnamed(t, name=name)
def toUps8(latlon, lon=None, datum=None, Ups=Ups, pole='', falsed=True, strict=True, name=''): '''Convert a lat-/longitude point to a UPS coordinate. @param latlon: Latitude (C{degrees}) or an (ellipsoidal) geodetic C{LatLon} point. @keyword lon: Optional longitude (C{degrees}) or C{None} if B{C{latlon}} is a C{LatLon}. @keyword datum: Optional datum for this UPS coordinate, overriding B{C{latlon}}'s datum (C{Datum}). @keyword Ups: Optional (sub-)class to return the UPS coordinate (L{Ups}) or C{None}. @keyword pole: Optional top/center of (stereographic) projection (C{str}, C{'N[orth]'} or C{'S[outh]'}). @keyword falsed: False both easting and northing (C{bool}). @keyword strict: Restrict B{C{lat}} to UPS ranges (C{bool}). @keyword name: Optional B{C{Ups}} name (C{str}). @return: The UPS coordinate (B{C{Ups}}) or a L{UtmUps8Tuple}C{(zone, hemipole, easting, northing, band, datum, convergence, scale)} if B{C{Ups}} is C{None}. The C{hemipole} is the C{'N'|'S'} pole, the UPS projection top/center. @raise RangeError: If B{C{strict}} and B{C{lat}} outside the valid UPS bands or if B{C{lat}} or B{C{lon}} outside the valid range and L{rangerrors} set to C{True}. @raise TypeError: If B{C{latlon}} is not ellipsoidal. @raise ValueError: If B{C{lon}} value is missing or if B{C{latlon}} is invalid. @see: Karney's C++ class U{UPS <https://GeographicLib.SourceForge.io/html/classGeographicLib_1_1UPS.html>}. ''' lat, lon, d, name = _to4lldn(latlon, lon, datum, name) z, B, p, lat, lon = upsZoneBand5(lat, lon, strict=strict) # PYCHOK UtmUpsLatLon5Tuple E = d.ellipsoid p = str(pole or p)[:1].upper() N = p == 'N' # is north a = lat if N else -lat A = abs(a - 90) < _TOL # at pole t = tan(radians(a)) T = E.es_taupf(t) r = hypot1(T) + abs(T) if T >= 0: r = 0 if A else 1 / r k0 = getattr(Ups, '_scale0', _K0) # Ups is class or None r *= 2 * k0 * E.a / E.es_c k = k0 if A else _scale(E, r, t) c = lon # [-180, 180) from .upsZoneBand5 x, y = sincos2d(c) x *= r y *= r if N: y = -y else: c = -c if falsed: x += _Falsing y += _Falsing if Ups is None: r = UtmUps8Tuple(z, p, x, y, B, d, c, k) else: if z != _UPS_ZONE and not strict: z = _UPS_ZONE # ignore UTM zone r = Ups(z, p, x, y, band=B, datum=d, falsed=falsed, convergence=c, scale=k) r._hemisphere = _hemi(lat) if isinstance(latlon, _LLEB) and d is latlon.datum: r._latlon_to(latlon, falsed) # XXX weakref(latlon)? return _xnamed(r, name)