def latlon2(self, datum=None): '''Convert this WM coordinate to a lat- and longitude. @kwarg datum: Optional, ellipsoidal datum (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}) or C{None}. @return: A L{LatLon2Tuple}C{(lat, lon)}. @raise TypeError: Invalid or non-ellipsoidal B{C{datum}}. @see: Method C{toLatLon}. ''' r = self.radius x = self._x / r y = 2 * atan(exp(self._y / r)) - PI_2 if datum is not None: E = _ellipsoidal_datum(datum, name=self.name).ellipsoid if not E.isEllipsoidal: raise _IsnotError(_ellipsoidal_, datum=datum) # <https://Earth-Info.NGA.mil/GandG/wgs84/web_mercator/ # %28U%29%20NGA_SIG_0011_1.0.0_WEBMERC.pdf> y = y / r if E.e: y -= E.e * atanh(E.e * tanh(y)) # == E.es_atanh(tanh(y)) y *= E.a x *= E.a / r r = LatLon2Tuple(Lat(degrees90(y)), Lon(degrees180(x))) return self._xnamed(r)
def flatLocal_(phi2, phi1, lam21, datum=Datums.WGS84): '''Compute the I{angular} distance between two (ellipsoidal) points using the U{ellipsoidal Earth to plane projection<https://WikiPedia.org/ wiki/Geographical_distance#Ellipsoidal_Earth_projected_to_a_plane>} aka U{Hubeny<https://www.OVG.AT/de/vgi/files/pdf/3781/>} formula. @arg phi2: End latitude (C{radians}). @arg phi1: Start latitude (C{radians}). @arg lam21: Longitudinal delta, M{end-start} (C{radians}). @kwarg datum: Ellipsoidal datum to use (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @return: Angular distance (C{radians}). @raise TypeError: Invalid B{C{datum}}. @note: The meridional and prime_vertical radii of curvature are taken and scaled I{at the mean of both latitude}. @see: Functions L{flatLocal}/L{hubeny}, L{cosineAndoyerLambert_}, L{cosineForsytheAndoyerLambert_}, L{cosineLaw_}, L{flatPolar_}, L{equirectangular_}, L{euclidean_}, L{haversine_}, L{thomas_} and L{vincentys_} and U{local, flat earth approximation <https://www.EdWilliams.org/avform.htm#flat>}. ''' E = _ellipsoidal_datum(datum, name=flatLocal_.__name__).ellipsoid m, n = E.roc2_((phi2 + phi1) * _0_5, scaled=True) return hypot(m * (phi2 - phi1), n * lam21)
def __init__(self, easting, northing, band=NN, datum=None, falsed=True, convergence=None, scale=None): '''(INTERNAL) New L{UtmUpsBase}. ''' E = self._Error if not E: notOverloaded(self, '_Error') self._easting = Easting(easting, Error=E) self._northing = Northing(northing, Error=E) if band: _xinstanceof(str, band=band) self._band = band if datum not in (None, self._datum): self._datum = _ellipsoidal_datum(datum) # XXX name=band if not falsed: self._falsed = False if convergence is not self._convergence: self._convergence = Scalar(convergence, name=_convergence_, Error=E) if scale is not self._scale: self._scale = Scalar(scale, name=_scale_, Error=E)
def __init__(self, lat0, lon0, datum=Datums.WGS84, name=NN): '''New L{CassiniSoldner} projection. @arg lat0: Latitude of center point (C{degrees90}). @arg lon0: Longitude of center point (C{degrees180}). @kwarg datum: Optional datum or ellipsoid (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @kwarg name: Optional name (C{str}). @raise CSSError: Invalid B{C{lat}} or B{C{lon}}. @raise ImportError: Package U{geographiclib<https://PyPI.org/ project/geographiclib>} missing. @example: >>> p = CassiniSoldner(48 + 50/60.0, 2 + 20/60.0) # Paris >>> p.forward(50.9, 1.8) # Calais (-37518.854545, 230003.561828) >>> p.reverse4(-38e3, 230e3) (50.899937, 1.793161, 89.580797, 0.999982) ''' if name: self.name = name if datum not in (None, self._datum): self._datum = _xellipsoidal( datum=_ellipsoidal_datum(datum, name=name)) self.reset(lat0, lon0)
def __init__(self, easting, northing, datum=None, name=NN): '''New L{Osgr} National Grid Reference. @arg easting: Easting from OS false easting (C{meter}). @arg northing: Northing from from OS false northing (C{meter}). @kwarg datum: Default datum (C{Datums.OSGB36}). @kwarg name: Optional name (C{str}). @raise OSGRError: Invalid or negative B{C{easting}} or B{C{northing}} or B{C{datum}} not C{Datums.OSBG36}. @example: >>> from pygeodesy import Osgr >>> r = Osgr(651409, 313177) ''' self._easting = Easting(easting, Error=OSGRError, osgr=True) self._northing = Northing(northing, Error=OSGRError, osgr=True) if datum not in (None, _Datums_OSGB36): try: if _ellipsoidal_datum(datum) != _Datums_OSGB36: raise ValueError except (TypeError, ValueError): raise OSGRError(datum=datum) if name: self.name = name
def _datum_setter(self, datum, knots): '''(INTERNAL) Set the datum. @raise TypeError: Invalid B{C{datum}}. ''' d = datum or getattr(knots[0], _datum_, datum) if d not in (None, self._datum): self._datum = _ellipsoidal_datum(d, name=self.name)
def thomas_(phi2, phi1, lam21, datum=Datums.WGS84): '''Compute the I{angular} distance between two (ellipsoidal) points using U{Thomas'<https://apps.DTIC.mil/dtic/tr/fulltext/u2/703541.pdf>} formula. @arg phi2: End latitude (C{radians}). @arg phi1: Start latitude (C{radians}). @arg lam21: Longitudinal delta, M{end-start} (C{radians}). @kwarg datum: Ellipsoidal datum to use (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @return: Angular distance (C{radians}). @raise TypeError: Invalid B{C{datum}}. @see: Functions L{thomas}, L{cosineAndoyerLambert_}, L{cosineForsytheAndoyerLambert_}, L{cosineLaw_}, L{equirectangular_}, L{euclidean_}, L{flatLocal_}/L{hubeny_}, L{flatPolar_}, L{haversine_} and L{vincentys_} and U{Geodesy-PHP <https://GitHub.com/jtejido/geodesy-php/blob/master/src/Geodesy/ Distance/ThomasFormula.php>}. ''' s2, c2, s1, c1, _, c21 = sincos2(phi2, phi1, lam21) E = _ellipsoidal_datum(datum, name=thomas_.__name__).ellipsoid if E.f and abs(c1) > EPS and abs(c2) > EPS: r1 = atan(E.b_a * s1 / c1) r2 = atan(E.b_a * s2 / c2) j = (r2 + r1) * _0_5 k = (r2 - r1) * _0_5 sj, cj, sk, ck, sl_2, _ = sincos2(j, k, lam21 * _0_5) h = fsum_(sk**2, (ck * sl_2)**2, -(sj * sl_2)**2) if EPS < abs(h) < EPS1: u = _1_0 / (_1_0 - h) d = atan(sqrt(h * u)) * 2 # == acos(1 - 2 * h) sd, cd = sincos2(d) if abs(sd) > EPS: u = 2 * (sj * ck)**2 * u v = 2 * (sk * cj)**2 / h x = u + v y = u - v t = d / sd s = 4 * t**2 e = 2 * cd a = s * e b = 2 * d c = t - (a - e) * _0_5 s = fsum_(a * x, c * x**2, -b * y, -e * y**2, s * x * y) * E.f / _16_0 s = fsum_(t * x, -y, -s) * E.f * _0_25 return d - s * sd # fall back to cosineLaw_ return acos(s1 * s2 + c1 * c2 * c21)
def datum(self, datum): '''Set the datum and ellipsoid (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @raise EllipticError: No convergence. @raise TypeError: Invalid B{C{datum}}. ''' d = _ellipsoidal_datum(datum, name=self.name, raiser=True) self._reset(d.ellipsoid) self._datum = d
def toDatum(self, datum): '''Convert this conic to the given datum. @arg datum: Ellipsoidal datum to use (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @return: Converted conic, unregistered (L{Conic}). @raise TypeError: Non-ellipsoidal B{C{datum}}. ''' d = _ellipsoidal_datum(datum, name=self.name) E = d.ellipsoid if not E.isEllipsoidal: raise _IsnotError(_ellipsoidal_, datum=datum) c = self if c._e != E.e or c._datum != d: c = Conic(None, 0, name=self._name) self._dup2(c) c._datum = d c._e = E.e if abs(c._par1 - c._par2) < EPS: m1 = c._mdef(c._phi0) t1 = c._tdef(c._phi0) t0 = t1 k = 1 # _1_0 n = sin(c._phi0) sp = 1 else: m1 = c._mdef(c._par1) m2 = c._mdef(c._par2) t1 = c._tdef(c._par1) t2 = c._tdef(c._par2) t0 = c._tdef(c._phi0) k = c._k0 n = (log(m1) - log(m2)) \ / (log(t1) - log(t2)) sp = 2 F = m1 / (n * pow(t1, n)) c._aF = k * E.a * F c._n = n c._n_ = _1_0 / n c._r0 = c._rdef(t0) c._SP = sp return c
def __init__(self, zone, en100k, easting, northing, band=NN, datum=Datums.WGS84, name=NN): '''New L{Mgrs} Military grid reference. @arg zone: 6° longitudinal zone (C{int}), 1..60 covering 180°W..180°E. @arg en100k: Two-letter EN digraph (C{str}), 100 km grid square. @arg easting: Easting (C{meter}), within 100 km grid square. @arg northing: Northing (C{meter}), within 100 km grid square. @kwarg band: Optional 8° latitudinal band (C{str}), C..X covering 80°S..84°N. @kwarg datum: Optional this reference's datum (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @kwarg name: Optional name (C{str}). @raise MGRSError: Invalid MGRS grid reference, B{C{zone}}, B{C{en100k}}, B{C{easting}}, B{C{northing}} or B{C{band}}. @raise TypeError: Invalid B{C{datum}}. @example: >>> from pygeodesy import Mgrs >>> m = Mgrs('31U', 'DQ', 48251, 11932) # 31U DQ 48251 11932 ''' if name: self.name = name self._zone, self._band, self._bandLat = _to3zBlat( zone, band, MGRSError) try: en = str(en100k).upper() if len(en) != 2: raise IndexError # caught below self._en100k = en self._en100k2m() except IndexError: raise MGRSError(en100k=en100k) self._easting = Easting(easting, Error=MGRSError) self._northing = Northing(northing, Error=MGRSError) if datum not in (None, self._datum): self._datum = _ellipsoidal_datum(datum, name=name)
def datum(self, datum): '''Set the datum and ellipsoid (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @raise EllipticError: No convergence. @raise TypeError: Invalid B{C{datum}}. ''' d = _ellipsoidal_datum(datum, name=self.name) E = d.ellipsoid self._reset(E.e, E.e2) self._a = E.a self._f = E.f # flattening = (a - b) / a self._datum = d self._E = E
def cosineForsytheAndoyerLambert_(phi2, phi1, lam21, datum=Datums.WGS84): '''Compute the I{angular} distance between two (ellipsoidal) points using the U{Forsythe-Andoyer-Lambert correction<https://www2.UNB.CA/gge/Pubs/TR77.pdf>} of the U{Law of Cosines<https://www.Movable-Type.co.UK/scripts/latlong.html#cosine-law>} formula. @arg phi2: End latitude (C{radians}). @arg phi1: Start latitude (C{radians}). @arg lam21: Longitudinal delta, M{end-start} (C{radians}). @kwarg datum: Ellipsoidal datum to use (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @return: Angular distance (C{radians}). @raise TypeError: Invalid B{C{datum}}. @see: Functions L{cosineForsytheAndoyerLambert}, L{cosineAndoyerLambert_}, L{cosineLaw_}, L{equirectangular_}, L{euclidean_}, L{flatLocal_}/L{hubeny_}, L{flatPolar_}, L{haversine_}, L{thomas_} and L{vincentys_} and U{Geodesy-PHP <https://GitHub.com/jtejido/geodesy-php/blob/master/src/Geodesy/ Distance/ForsytheCorrection.php>}. ''' s2, c2, s1, c1, _, c21 = sincos2(phi2, phi1, lam21) r = acos(s1 * s2 + c1 * c2 * c21) E = _ellipsoidal_datum(datum, name=cosineForsytheAndoyerLambert_.__name__).ellipsoid if E.f: sr, cr, s2r, _ = sincos2(r, r * 2) if abs(sr) > EPS: r2 = r**2 p = (s1 + s2)**2 / (1 + cr) q = (s1 - s2)**2 / (1 - cr) x = p + q y = p - q s = 8 * r2 / sr a = 64 * r + 2 * s * cr # 16 * r2 / tan(r) d = 48 * sr + s # 8 * r2 / tan(r) b = -2 * d e = 30 * s2r c = fsum_(30 * r, e / 2, s * cr) # 8 * r2 / tan(r) d = fsum_(a * x, b * y, -c * x**2, d * x * y, e * y**2) * E.f / _32_0 d = fsum_(d, -x * r, 3 * y * sr) * E.f * _0_25 r += d return r
def __init__(self, lat, lon, height=0, datum=None, reframe=None, epoch=None, name=NN): '''Create an ellipsoidal C{LatLon} point frome the given lat-, longitude and height on the given datum and with the given reference frame and epoch. @arg lat: Latitude (C{degrees} or DMS C{[N|S]}). @arg lon: Longitude (C{degrees} or DMS C{str[E|W]}). @kwarg height: Optional elevation (C{meter}, the same units as the datum's half-axes). @kwarg datum: Optional, ellipsoidal datum to use (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @kwarg reframe: Optional reference frame (L{RefFrame}). @kwarg epoch: Optional epoch to observe for B{C{reframe}} (C{scalar}), a non-zero, fractional calendar year. @kwarg name: Optional name (string). @raise RangeError: Value of B{C{lat}} or B{C{lon}} outside the valid range and C{rangerrors} set to C{True}. @raise TypeError: B{C{datum}} is not a L{datum}, B{C{reframe}} is not a L{RefFrame} or B{C{epoch}} is not C{scalar} non-zero. @raise UnitError: Invalid B{C{lat}}, B{C{lon}} or B{C{height}}. @example: >>> p = LatLon(51.4778, -0.0016) # height=0, datum=Datums.WGS84 ''' LatLonBase.__init__(self, lat, lon, height=height, name=name) if datum not in (None, self._datum): self.datum = _ellipsoidal_datum(datum, name=name) if reframe: self.reframe = reframe self.epoch = epoch
def convertDatum(self, datum2): '''Convert this point to an other datum. @arg datum2: Datum to convert I{to} (L{Datum}). @return: The converted point (ellipsoidal C{LatLon}). @raise TypeError: The B{C{datum2}} invalid. @example: >>> p = LatLon(51.4778, -0.0016) # default Datums.WGS84 >>> p.convertDatum(Datums.OSGB36) # 51.477284°N, 000.00002°E ''' d2 = _ellipsoidal_datum(datum2, name=self.name) if self.datum == d2: return self.copy() c = self.toCartesian().convertDatum(d2) return c.toLatLon(datum=d2, LatLon=self.classof)
def cosineAndoyerLambert_(phi2, phi1, lam21, datum=Datums.WGS84): '''Compute the I{angular} distance between two (ellipsoidal) points using the U{Andoyer-Lambert correction<https://navlib.net/wp-content/uploads/2013/10/ admiralty-manual-of-navigation-vol-1-1964-english501c.pdf>} of the U{Law of Cosines<https://www.Movable-Type.co.UK/scripts/latlong.html#cosine-law>} fromula. @arg phi2: End latitude (C{radians}). @arg phi1: Start latitude (C{radians}). @arg lam21: Longitudinal delta, M{end-start} (C{radians}). @kwarg datum: Ellipsoidal datum to use (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @return: Angular distance (C{radians}). @raise TypeError: Invalid B{C{datum}}. @see: Functions L{cosineAndoyerLambert}, L{cosineForsytheAndoyerLambert_}, L{cosineLaw_}, L{equirectangular_}, L{euclidean_}, L{flatLocal_}/L{hubeny_}, L{flatPolar_}, L{haversine_}, L{thomas_} and L{vincentys_} and U{Geodesy-PHP <https://GitHub.com/jtejido/geodesy-php/blob/master/src/Geodesy/ Distance/AndoyerLambert.php>}. ''' s2, c2, s1, c1, _, c21 = sincos2(phi2, phi1, lam21) if c2 and c1: E = _ellipsoidal_datum(datum, name=cosineAndoyerLambert_.__name__).ellipsoid if E.f and abs(c1) > EPS and abs(c2) > EPS: r2 = atan(E.b_a * s2 / c2) r1 = atan(E.b_a * s1 / c1) s2, c2, s1, c1 = sincos2(r2, r1) r = acos(s1 * s2 + c1 * c2 * c21) sr, _, sr_2, cr_2 = sincos2(r, r * _0_5) if abs(sr_2) > EPS and abs(cr_2) > EPS: c = (sr - r) * ((s1 + s2) / cr_2)**2 s = (sr + r) * ((s1 - s2) / sr_2)**2 r += E.f * (c - s) * _0_125 return r # fall back to cosineLaw_ return acos(s1 * s2 + c1 * c2 * c21)
def __init__(self, xyz, y=None, z=None, datum=None, ll=None, name=NN): '''New C{Cartesian...}. @arg xyz: An L{Ecef9Tuple}, L{Vector3Tuple}, L{Vector4Tuple} or the C{X} coordinate (C{scalar}). @arg y: The C{Y} coordinate (C{scalar}) if B{C{xyz}} C{scalar}. @arg z: The C{Z} coordinate (C{scalar}) if B{C{xyz}} C{scalar}. @kwarg datum: Optional datum (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @kwarg ll: Optional, original latlon (C{LatLon}). @kwarg name: Optional name (C{str}). @raise TypeError: Non-scalar B{C{xyz}}, B{C{y}} or B{C{z}} coordinate or B{C{xyz}} not an L{Ecef9Tuple}, L{Vector3Tuple} or L{Vector4Tuple}. ''' x, y, z, h, d, n = _xyzhdn6(xyz, y, z, None, datum, ll) Vector3d.__init__(self, x, y, z, ll=ll, name=name or n) if h: self._height = h if d: self.datum = _ellipsoidal_datum(d, name=name)
def __init__(self, x, y, z, h=0, datum=None, ll=None, name=NN): '''New n-vector normal to the earth's surface. @arg x: X component (C{meter}). @arg y: Y component (C{meter}). @arg z: Z component (C{meter}). @kwarg h: Optional height above model surface (C{meter}). @kwarg datum: Optional datum this n-vector is defined in (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @kwarg ll: Optional, original latlon (C{LatLon}). @kwarg name: Optional name (C{str}). @raise TypeError: If B{C{datum}} is not a L{Datum}. @example: >>> from ellipsoidalNvector import Nvector >>> v = Nvector(0.5, 0.5, 0.7071, 1) >>> v.toLatLon() # 45.0°N, 045.0°E, +1.00m ''' NvectorBase.__init__(self, x, y, z, h=h, ll=ll, name=name) if datum not in (None, self._datum): self._datum = _ellipsoidal_datum(datum, name=name)
def toNvector(self, Nvector=None, datum=None, **Nvector_kwds): # PYCHOK Datums.WGS84 '''Convert this cartesian to C{n-vector} components. @kwarg Nvector: Optional class to return the C{n-vector} components (C{Nvector}) or C{None}. @kwarg datum: Optional datum (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}) overriding this cartesian's datum. @kwarg Nvector_kwds: Optional, additional B{C{Nvector}} keyword arguments, ignored if B{C{Nvector=None}}. @return: The C{unit, n-vector} components (B{C{Nvector}}) or a L{Vector4Tuple}C{(x, y, z, h)} if B{C{Nvector}} is C{None}. @raise TypeError: Invalid B{C{datum}}. @raise ValueError: The B{C{Cartesian}} at origin. @example: >>> c = Cartesian(3980581, 97, 4966825) >>> n = c.toNvector() # (x=0.622818, y=0.00002, z=0.782367, h=0.242887) ''' d = _ellipsoidal_datum(datum or self.datum, name=self.name) r = self._v4t if r is None or d != self.datum: # <https://www.Movable-Type.co.UK/scripts/geodesy/docs/ # latlon-nvector-ellipsoidal.js.html#line309> E = d.ellipsoid x, y, z = self.xyz # Kenneth Gade eqn 23 p = hypot2(x, y) * E.a2_ q = (z**2 * E.e12) * E.a2_ r = fsum_(p, q, -E.e4) / 6 s = (p * q * E.e4) / (4 * r**3) t = cbrt(fsum_(1, s, sqrt(s * (2 + s)))) u = r * fsum_(_1_0, t, _1_0 / t) v = sqrt(u**2 + E.e4 * q) w = E.e2 * fsum_(u, v, -q) / (2 * v) k = sqrt(fsum_(u, v, w**2)) - w if abs(k) < EPS: raise _ValueError(origin=self) e = k / (k + E.e2) # d = e * hypot(x, y) # tmp = 1 / hypot(d, z) == 1 / hypot(e * hypot(x, y), z) t = hypot_(e * x, e * y, z) # == 1 / tmp if t < EPS: raise _ValueError(origin=self) h = fsum_(k, E.e2, -_1_0) / k * t s = e / t # == e * tmp r = Vector4Tuple(x * s, y * s, z / t, h) self._v4t = r if d == self.datum else None if Nvector is not None: r = Nvector(r.x, r.y, r.z, h=r.h, datum=d, **Nvector_kwds) return self._xnamed(r)
def toUtm8(latlon, lon=None, datum=None, Utm=Utm, falsed=True, name=NN, zone=None, **cmoff): '''Convert a lat-/longitude point to a UTM coordinate. @arg latlon: Latitude (C{degrees}) or an (ellipsoidal) geodetic C{LatLon} point. @kwarg lon: Optional longitude (C{degrees}) or C{None}. @kwarg datum: Optional datum for this UTM coordinate, overriding B{C{latlon}}'s datum (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @kwarg Utm: Optional class to return the UTM coordinate (L{Utm}) or C{None}. @kwarg falsed: False both easting and northing (C{bool}). @kwarg name: Optional B{C{Utm}} name (C{str}). @kwarg zone: Optional UTM zone to enforce (C{int} or C{str}). @kwarg cmoff: DEPRECATED, use B{C{falsed}}. Offset longitude from the zone's central meridian (C{bool}). @return: The UTM coordinate (B{C{Utm}}) or if B{C{Utm}} is C{None} or not B{C{falsed}}, a L{UtmUps8Tuple}C{(zone, hemipole, easting, northing, band, datum, convergence, scale)}. The C{hemipole} is the C{'N'|'S'} hemisphere. @raise RangeError: If B{C{lat}} outside the valid UTM bands or if B{C{lat}} or B{C{lon}} outside the valid range and L{rangerrors} set to C{True}. @raise TypeError: Invalid B{C{datum}} or B{C{latlon}} not ellipsoidal. @raise UTMError: Invalid B{C{zone}}. @raise ValueError: If B{C{lon}} value is missing or if B{C{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 ''' z, B, lat, lon, d, f, name = _to7zBlldfn(latlon, lon, datum, falsed, name, zone, UTMError, **cmoff) d = _ellipsoidal_datum(d, name=name) E = d.ellipsoid a, b = radians(lat), radians(lon) # easting, northing: Karney 2011 Eq 7-14, 29, 35 sb, cb = sincos2(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 = E.A * getattr(Utm, '_scale0', _K0) # Utm is class or None K = _Kseries(E.AlphaKs, x, y) # Krüger series y = K.ys(y) * A0 # ξ x = K.xs(x) * A0 # η # convergence: Karney 2011 Eq 23, 24 p_ = K.ps(1) q_ = K.qs(0) c = degrees(atan(T_ / hypot1(T_) * tan(b)) + atan2(q_, p_)) # scale: Karney 2011 Eq 25 k = E.e2s(sin(a)) * T12 / H * (A0 / E.a * hypot(p_, q_)) return _toXtm8(Utm, z, lat, x, y, B, d, c, k, f, name, latlon, EPS)
def __init__(self, sa1, ca1, sa2, ca2, k, datum, name): '''(INTERNAL) New C{AlbersEqualArea...} instance. ''' if datum not in (None, self._datum): self._datum = _ellipsoidal_datum(datum, name=name) if name: self.name = name E = self.datum.ellipsoid b_a = E.b_a # fm = 1 - E.f e2 = E.e2 e12 = E.e12 # e2m = 1 - E.e2 self._qZ = qZ = _1_0 + e12 * self._atanhee(1) self._qZa2 = qZ * E.a2 self._qx = qZ / (_2_0 * e12) c = min(ca1, ca2) if c < 0: raise AlbersError(clat1=ca1, clat2=ca2) polar = c < _EPS__2 # == 0 # determine hemisphere of tangent latitude if sa1 < 0: # and sa2 < 0: self._sign = -1 # internally, tangent latitude positive sa1, sa2 = -sa1, neg(sa2) if sa1 > sa2: # make phi1 < phi2 sa1, sa2 = sa2, sa1 ca1, ca2 = ca2, ca1 if sa1 < 0: # or sa2 < 0: raise AlbersError(slat1=sa1, slat2=sa2) # avoid singularities at poles ca1, ca2 = max(_EPS__2, ca1), max(_EPS__2, ca2) ta1, ta2 = sa1 / ca1, sa2 / ca2 par1 = abs(ta1 - ta2) < _EPS__4 # ta1 == ta2 if par1 or polar: C, ta0 = _1_0, ta2 else: s1_qZ, C = self._s1_qZ_C2(ca1, sa1, ta1, ca2, sa2, ta2) ta0 = (ta2 + ta1) * _0_5 Ta0 = Fsum(ta0) tol = _tol(_TOL0, ta0) for self._iteration in range(1, _NUMIT0): ta02 = ta0**2 sca02 = ta02 + _1_0 sca0 = sqrt(sca02) sa0 = ta0 / sca0 sa01 = sa0 + _1_0 sa02 = sa0**2 # sa0m = 1 - sa0 = 1 / (sec(a0) * (tan(a0) + sec(a0))) sa0m = _1_0 / (sca0 * (ta0 + sca0)) # scb0^2 * sa0 g = (_1_0 + (b_a * ta0)**2) * sa0 dg = e12 * sca02 * (_1_0 + 2 * ta02) + e2 D = sa0m * (_1_0 - e2 * (_1_0 + sa01 * 2 * sa0)) / (e12 * sa01) # dD/dsa0 dD = -2 * (_1_0 - e2 * sa02 * (_3_0 + 2 * sa0)) / (e12 * sa01**2) sa02_ = _1_0 - e2 * sa02 sa0m_ = sa0m / (_1_0 - e2 * sa0) BA = sa0m_ * (self._atanhx1(e2 * sa0m_**2) * e12 - e2 * sa0m) \ - sa0m**2 * e2 * (2 + (_1_0 + e2) * sa0) / (e12 * sa02_) # == B + A dAB = 2 * e2 * (2 - e2 * (_1_0 + sa02)) / (e12 * sa02_**2 * sca02) u_du = fsum_(s1_qZ * g, -D, g * BA) \ / fsum_(s1_qZ * dg, -dD, dg * BA, g * dAB) # == u/du ta0, d = Ta0.fsum2_(-u_du * (sca0 * sca02)) if abs(d) < tol: break else: raise AlbersError(iteration=_NUMIT0, txt=_no_(Fmt.convergence(tol))) self._txi0 = txi0 = self._txif(ta0) self._scxi0 = hypot1(txi0) self._sxi0 = sxi0 = txi0 / self._scxi0 self._m02 = m02 = _1_0 / (_1_0 + (b_a * ta0)**2) self._n0 = n0 = ta0 / hypot1(ta0) if polar: self._polar = True self._nrho0 = self._m0 = _0_0 else: self._m0 = sqrt(m02) # == nrho0 / E.a self._nrho0 = E.a * self._m0 # == E.a * sqrt(m02) self._k0_(_1_0 if par1 else (k * sqrt(C / (m02 + n0 * qZ * sxi0)))) self._lat0 = _Lat(lat0=self._sign * atand(ta0))
def toUps8(latlon, lon=None, datum=None, Ups=Ups, pole=NN, falsed=True, strict=True, name=NN): '''Convert a lat-/longitude point to a UPS coordinate. @arg latlon: Latitude (C{degrees}) or an (ellipsoidal) geodetic C{LatLon} point. @kwarg lon: Optional longitude (C{degrees}) or C{None} if B{C{latlon}} is a C{LatLon}. @kwarg datum: Optional datum for this UPS coordinate, overriding B{C{latlon}}'s datum (C{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @kwarg Ups: Optional class to return the UPS coordinate (L{Ups}) or C{None}. @kwarg pole: Optional top/center of (stereographic) projection (C{str}, C{'N[orth]'} or C{'S[outh]'}). @kwarg falsed: False both easting and northing (C{bool}). @kwarg strict: Restrict B{C{lat}} to UPS ranges (C{bool}). @kwarg 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 or B{C{datum}} invalid. @raise ValueError: If B{C{lon}} value is missing or if B{C{latlon}} is invalid. @see: I{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 d = _ellipsoidal_datum(d, name=name) 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, Error=UPSError) 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)