def toWm(latlon, lon=None, radius=R_MA, Wm=Wm, name=''): '''Convert a lat-/longitude point to a WM coordinate. @param latlon: Latitude (C{degrees}) or an (ellipsoidal or spherical) geodetic C{LatLon} point. @keyword lon: Optional longitude (C{degrees} or C{None}). @keyword radius: Optional earth radius (C{meter}). @keyword Wm: Optional (sub-)class for the WM coordinate (L{Wm}) or C{None}. @keyword name: Optional name (C{str}). @return: The WM coordinate (L{Wm}) or 3-tuple (easting, northing, radius) if I{Wm} is C{None}. @raise ValueError: If I{lon} value is missing, if I{latlon} is not scalar, if I{latlon} is beyond the valid WM range and L{rangerrors} is set to C{True} or if I{radius} is invalid. @example: >>> p = LatLon(48.8582, 2.2945) # 448251.8 5411932.7 >>> w = toWm(p) # 448252 5411933 >>> p = LatLon(13.4125, 103.8667) # 377302.4 1483034.8 >>> w = toWm(p) # 377302 1483035 ''' r, e = radius, None try: lat, lon = latlon.lat, latlon.lon if isinstance(latlon, _ELLB): r = latlon.datum.ellipsoid.a e = latlon.datum.ellipsoid.e if not name: # use latlon.name name = _nameof(latlon) or name # PYCHOK no effect lat = clipDMS(lat, _LatLimit) except AttributeError: lat, lon = parseDMS2(latlon, lon, clipLat=_LatLimit) s = sin(radians(lat)) y = atanh(s) # == log(tan((90 + lat) / 2)) == log(tanPI_2_2(radians(lat))) if e: y -= e * atanh(e * s) e, n = r * radians(lon), r * y return (e, n, r) if Wm is None else _xnamed(Wm(e, n, radius=r), name)
def toCss(latlon, cs0=_CassiniSoldner0, height=None, Css=Css, name=''): '''Convert an (ellipsoidal) geodetic point to a Cassini-Soldner location. @param latlon: Ellipsoidal point (C{LatLon}). @keyword cs0: Optional, the Cassini-Soldner projection to use (L{CassiniSoldner}). @keyword height: Optional height for the point, overriding the default height (C{meter}). @keyword Css: Optional (sub-)class to return the location (L{Css}) or C{None}. @keyword name: Optional I{Css} name (C{str}). @return: The Cassini-Soldner location (L{Css}) or 3-tuple (C{easting, northing, height}) if I{Css} is C{None}. @raise ImportError: Package U{GeographicLib<http://PyPI.org/ project/geographiclib>} missing. @raise TypeError: If I{latlon} is not ellipsoidal. ''' if not isinstance(latlon, _LLEB): raise TypeError('%s not %s: %r' % ('latlon', 'ellipsoidal', latlon)) cs = _CassiniSoldner(cs0) C, E = cs.datum.ellipsoid, latlon.datum.ellipsoid if C.a != E.a or C.f != E.f: raise ValueError('%s mistmatch %r vs %r' % ('ellipsoidal', C, E)) e, n, z, rk = cs.forward4(latlon.lat, latlon.lon) h = latlon.height if height is None else height if Css is None: r = e, n, h else: r = _xnamed(Css(e, n, h=h, cs0=cs), name or _nameof(latlon)) r._latlon = latlon.lat, latlon.lon r._azi, r._rk = z, rk return r
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 toUtm(latlon, lon=None, datum=None, Utm=Utm, name='', cmoff=True): '''Convert a lat-/longitude point to a UTM coordinate. @param latlon: Latitude (C{degrees}) or an (ellipsoidal) geodetic C{LatLon} point. @keyword lon: Optional longitude (C{degrees} or C{None}). @keyword datum: Optional datum for this UTM coordinate, overriding I{latlon}'s datum (C{Datum}). @keyword Utm: Optional (sub-)class to use for the UTM coordinate (L{Utm}) or C{None}. @keyword name: Optional I{Utm} name (C{str}). @keyword cmoff: Offset longitude from zone's central meridian, apply false easting and false northing (C{bool}). @return: The UTM coordinate (L{Utm}) or a 6-tuple (zone, easting, northing, band, convergence, scale) if I{Utm} is C{None} or I{cmoff} is C{False}. @raise TypeError: If I{latlon} is not ellipsoidal. @raise RangeError: If I{lat} is outside the valid UTM bands or if I{lat} or I{lon} outside the valid range and I{rangerrrors} set to C{True}. @raise ValueError: If I{lon} value is missing or if I{latlon} is invalid. @note: Implements Karney’s method, using 8-th order Krüger series, giving results accurate to 5 nm (or better) for distances up to 3900 km from the central meridian. @example: >>> p = LatLon(48.8582, 2.2945) # 31 N 448251.8 5411932.7 >>> u = toUtm(p) # 31 N 448252 5411933 >>> p = LatLon(13.4125, 103.8667) # 48 N 377302.4 1483034.8 >>> u = toUtm(p) # 48 N 377302 1483035 ''' try: lat, lon = latlon.lat, latlon.lon if not isinstance(latlon, _LLEB): raise TypeError('%s not %s: %r' % ('latlon', 'ellipsoidal', latlon)) if not name: # use latlon.name name = _nameof(latlon) or name # PYCHOK no effect d = datum or latlon.datum except AttributeError: lat, lon = parseDMS2(latlon, lon) d = datum or Datums.WGS84 E = d.ellipsoid z, B, a, b = _toZBab4(lat, lon, cmoff) # easting, northing: Karney 2011 Eq 7-14, 29, 35 cb, sb, tb = cos(b), sin(b), tan(b) T = tan(a) T12 = hypot1(T) S = sinh(E.e * atanh(E.e * T / T12)) T_ = T * hypot1(S) - S * T12 H = hypot(T_, cb) y = atan2(T_, cb) # ξ' ksi x = asinh(sb / H) # η' eta A0 = _K0 * E.A Ks = _Kseries(E.AlphaKs, x, y) # Krüger series y = Ks.ys(y) * A0 # ξ x = Ks.xs(x) * A0 # η if cmoff: # C.F.F. Karney, "Test data for the transverse Mercator projection (2009)", # <http://GeographicLib.SourceForge.io/html/transversemercator.html> and # <http://Zenodo.org/record/32470#.W4LEJS2ZON8> x += _FalseEasting # make x relative to false easting if y < 0: y += _FalseNorthing # y relative to false northing in S # convergence: Karney 2011 Eq 23, 24 p_ = Ks.ps(1) q_ = Ks.qs(0) c = degrees(atan(T_ / hypot1(T_) * tb) + atan2(q_, p_)) # scale: Karney 2011 Eq 25 s = E.e2s(sin(a)) * T12 / H * (A0 / E.a * hypot(p_, q_)) if cmoff and Utm is not None: h = 'S' if a < 0 else 'N' # hemisphere return _xnamed( Utm(z, h, x, y, band=B, datum=d, convergence=c, scale=s), name) else: # zone, easting, northing, band, convergence and scale return z, x, y, B, c, s