Coverage for pygeodesy/etm.py : 97%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# -*- coding: utf-8 -*-
Classes L{Etm}, L{ETMError} and L{ExactTransverseMercator}, transcoded from I{Karney}'s C++ class U{TransverseMercatorExact<https://GeographicLib.SourceForge.io/C++/doc/ classGeographicLib_1_1TransverseMercatorExact.html>}, abbreviated as C{TMExact} below.
Class L{ExactTransverseMercator} provides C{Exact Transverse Mercator} projections while instances of class L{Etm} represent ETM C{(easting, northing)} locations. See also I{Karney}'s utility U{TransverseMercatorProj<https://GeographicLib.SourceForge.io/C++/doc/ TransverseMercatorProj.1.html>}.
Following is a copy of I{Karney}'s U{TransverseMercatorExact.hpp <https://GeographicLib.SourceForge.io/C++/doc/TransverseMercatorExact_8hpp_source.html>} file C{Header}.
Copyright (C) U{Charles Karney<mailto:Charles@Karney.com>} (2008-2022) and licensed under the MIT/X11 License. For more information, see the U{GeographicLib<https:// GeographicLib.SourceForge.io>} documentation.
The method entails using the U{Thompson Transverse Mercator<https://WikiPedia.org/ wiki/Transverse_Mercator_projection>} as an intermediate projection. The projections from the intermediate coordinates to C{phi, lam} and C{x, y} are given by elliptic functions. The inverse of these projections are found by Newton's method with a suitable starting guess.
The relevant section of L.P. Lee's paper U{Conformal Projections Based On Jacobian Elliptic Functions<https://DOI.org/10.3138/X687-1574-4325-WM62>} in part V, pp 67-101. The C++ implementation and notation closely follow Lee, with the following exceptions::
Lee here Description
x/a xi Northing (unit Earth)
y/a eta Easting (unit Earth)
s/a sigma xi + i * eta
y x Easting
x y Northing
k e Eccentricity
k^2 mu Elliptic function parameter
k'^2 mv Elliptic function complementary parameter
m k Scale
zeta zeta Complex longitude = Mercator = chi in paper
s sigma Complex GK = zeta in paper
Minor alterations have been made in some of Lee's expressions in an attempt to control round-off. For example, C{atanh(sin(phi))} is replaced by C{asinh(tan(phi))} which maintains accuracy near C{phi = pi/2}. Such changes are noted in the code. ''' # make sure int/int division yields float quotient, see .basics
_1_EPS, isnear0, _0_0, _0_1, _0_5, \ _1_0, _2_0, _3_0, _4_0, _90_0, _180_0 # from pygeodesy.errors import _incompatible # from .named _spherical_, _usage _tand, _unsigned2 # from pygeodesy.lazily import _ALL_LAZY # from .elliptic Property_RO, property_RO, _update_all, \ property_doc_ _to7zBlldfn, Utm, UTMError
'''(INTERNAL) Like C{copysign0(OVERFLOW, B{x})}. '''
'''Exact Transverse Mercator (ETM) parse, projection or other L{Etm} issue or L{ExactTransverseMercator} conversion failure. '''
'''Exact Transverse Mercator (ETM) coordinate, a sub-class of L{Utm}, a Universal Transverse Mercator (UTM) coordinate using the L{ExactTransverseMercator} projection for highest accuracy.
@note: Conversion of (geodetic) lat- and longitudes to/from L{Etm} coordinates is 3-4 times slower than to/from L{Utm}.
@see: Karney's U{Detailed Description<https://GeographicLib.SourceForge.io/ html/classGeographicLib_1_1TransverseMercatorExact.html#details>}. '''
datum=_WGS84, falsed=True, convergence=None, scale=None, name=NN): '''New L{Etm} coordinate.
@arg zone: Longitudinal UTM zone (C{int}, 1..60) or zone with/-out I{latitudinal} Band letter (C{str}, '01C'|..|'60X'). @arg hemisphere: Northern or southern hemisphere (C{str}, C{'N[orth]'} or C{'S[outh]'}). @arg easting: Easting, see B{C{falsed}} (C{meter}). @arg northing: Northing, see B{C{falsed}} (C{meter}). @kwarg band: Optional, I{latitudinal} band (C{str}, 'C'|..|'X'). @kwarg datum: Optional, this coordinate's datum (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @kwarg falsed: If C{True}, both B{C{easting}} and B{C{northing}} are C{falsed} (C{bool}). @kwarg convergence: Optional meridian convergence, bearing off grid North, clockwise from true North (C{degrees}) or C{None}. @kwarg scale: Optional grid scale factor (C{scalar}) or C{None}. @kwarg name: Optional name (C{str}).
@raise ETMError: Invalid B{C{zone}}, B{C{hemishere}} or B{C{band}} or near-spherical B{C{datum}} or C{ellipsoid}.
@raise TypeError: Invalid or near-spherical B{C{datum}}.
@example:
>>> import pygeodesy >>> u = pygeodesy.Etm(31, 'N', 448251, 5411932) ''' band=band, datum=datum, falsed=falsed, convergence=convergence, scale=scale, name=name)
'''Get the ETM projection (L{ExactTransverseMercator}). '''
'''Set the ETM projection (L{ExactTransverseMercator}).
@raise ETMError: The B{C{exacTM}}'s datum incompatible with this ETM coordinate's C{datum}. '''
raise ETMError(repr(exactTM), txt=_incompatible(repr(E)))
'''Parse a string to a similar L{Etm} instance.
@arg strETM: The ETM coordinate (C{str}), see function L{parseETM5}. @kwarg name: Optional instance name (C{str}), overriding this name.
@return: The instance (L{Etm}).
@raise ETMError: Invalid B{C{strETM}}.
@see: Function L{pygeodesy.parseUPS5}, L{pygeodesy.parseUTM5} and L{pygeodesy.parseUTMUPS5}. ''' name=name or self.name)
def parseETM(self, strETM): # PYCHOK no cover '''DEPRECATED, use method L{Etm.parse}. ''' return self.parse(strETM)
'''Convert this ETM coordinate to an (ellipsoidal) geodetic point.
@kwarg LatLon: Optional, ellipsoidal class to return the geodetic point (C{LatLon}) or C{None}. @kwarg unfalse: Unfalse B{C{easting}} and B{C{northing}} if C{falsed} (C{bool}).
@return: This ETM coordinate as (B{C{LatLon}}) or a L{LatLonDatum5Tuple}C{(lat, lon, datum, convergence, scale)} if B{C{LatLon}} is C{None}.
@raise ETMError: No convergence transforming to lat- and longitude.
@raise ETMError: This ETM coordinate's C{exacTM} and C{datum} incompatible.
@raise TypeError: Invalid or non-ellipsoidal B{C{LatLon}}.
@example:
>>> from pygeodesy import ellipsoidalVincenty as eV, Etm >>> u = Etm(31, 'N', 448251.795, 5411932.678) >>> ll = u.toLatLon(eV.LatLon) # 48°51′29.52″N, 002°17′40.20″E '''
'''(INTERNAL) Compute (ellipsoidal) lat- and longitude. ''' # double check that this and exactTM's ellipsoid match if xTM._E != d.ellipsoid: # PYCHOK no cover t = repr(d.ellipsoid) raise ETMError(repr(xTM._E), txt=_incompatible(t))
'''Copy this ETM to a UTM coordinate.
@return: The UTM coordinate (L{Utm}). '''
'''A Python version of Karney's U{TransverseMercatorExact <https://GeographicLib.SourceForge.io/C++/doc/TransverseMercatorExact_8cpp_source.html>} C++ class, a numerically exact transverse Mercator projection, here referred to as C{TMExact}. ''' # _iteration = None # ._sigmaInv2 and ._zetaInv2
'''New L{ExactTransverseMercator} projection.
@kwarg datum: The I{non-spherical} datum or ellipsoid (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @kwarg lon0: Central meridian, default (C{degrees180}). @kwarg k0: Central scale factor (C{float}). @kwarg extendp: Use the I{extended} domain (C{bool}), I{standard} otherwise. @kwarg name: Optional name for the projection (C{str}). @kwarg raiser: If C{True}, throw an L{ETMError} for convergence failures (C{bool}).
@raise ETMError: Near-spherical B{C{datum}} or C{ellipsoid} or invalid B{C{lon0}} or B{C{k0}}.
@raise TypeError: Invalid or near-spherical B{C{datum}}.
@see: U{Constructor TransverseMercatorExact<https://GeographicLib.SourceForge.io/ html/classGeographicLib_1_1TransverseMercatorExact.html} for more details, especially on B{X{extendp}}.
@note: For all 255.5K U{TMcoords.dat<https://Zenodo.org/record/32470>} tests (with C{0 <= lat <= 84} and C{0 <= lon}) the maximum error is C{5.2e-08 .forward} (or 52 nano-meter) easting and northing and C{3.8e-13 .reverse} (or 0.38 pico-degrees) lat- and longitude (with Python 3.7.3+, 2.7.16+, PyPy6 3.5.3 and PyPy6 2.7.13, all in 64-bit on macOS 10.13.6 High Sierra C{x86_64} and 12.2 Monterey C{arm64} and C{"arm64_x86_64"}). ''' self.raiser = True
'''Get the datum (L{Datum}) or C{None}. '''
'''Set the datum and ellipsoid (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}).
@raise ETMError: Near-spherical B{C{datum}} or C{ellipsoid}.
@raise TypeError: Invalid or near-spherical B{C{datum}}. '''
'''(INTERNAL) Get and cache C{_e}. '''
def _1_e_90(self): # PYCHOK no cover '''(INTERNAL) Get and cache C{(1 - _e) * 90}. ''' return (_1_0 - self._e) * _90_0
'''Get the ellipsoid (L{Ellipsoid}). '''
'''(INTERNAL) Get and cache C{_e * PI / 2}. '''
'''(INTERNAL) Get and cache C{- _e * PI / 4}. '''
'''(INTERNAL) Get and cache C{(1 - _e) * PI / 2}. '''
'''(INTERNAL) Get and cache C{(1 - 2 * _e) * PI / 2}. '''
'''Get this C{ellipsoid}'s equatorial radius, semi-axis (C{meter}). '''
'''(INTERNAL) Get and cache C{e * TAYTOL}. '''
'''(INTERNAL) Get and cache C{Elliptic(_mu)}. '''
'''(INTERNAL) Get and cache C{_Eu.cE}. '''
'''(INTERNAL) Return C{_Eu.cE * 2 - B{xi}}. '''
'''(INTERNAL) Get and cache C{_Eu.cE / 4}. '''
'''(INTERNAL) Get and cache C{_Eu.cK}. '''
'''(INTERNAL) Get and cache C{_Eu.cK / _Eu.cE}. '''
'''(INTERNAL) Get and cache C{_Eu.cK * 2 / PI}. '''
'''(INTERNAL) Get and cache C{Elliptic(_mv)}. '''
'''(INTERNAL) Get and cache C{_Ev.cK}. '''
'''(INTERNAL) Get and cache C{_Ev.cKE}. '''
'''(INTERNAL) Get and cache C{_Ev.cKE * 3 / 4}. '''
'''(INTERNAL) Get and cache C{_Ev.cKE * 5 / 4}. '''
'''Get the domain (C{bool}), I{extended} or I{standard}. '''
'''Get this C{ellipsoid}'s flattening (C{float}). ''' return self._E.f
'''Forward projection, from geographic to transverse Mercator.
@arg lat: Latitude of point (C{degrees}). @arg lon: Longitude of point (C{degrees}). @kwarg lon0: Central meridian (C{degrees180}), overriding the default if not C{None}. @kwarg name: Optional name (C{str}).
@return: L{Forward4Tuple}C{(easting, northing, convergence, scale)}.
@see: C{void TMExact::Forward(real lon0, real lat, real lon, real &x, real &y, real &gamma, real &k)}.
@raise ETMError: No convergence, thrown if property C{raiser} set, ''' else: # enforce the parity if backside: # PYCHOK no cover lon = _180_0 - lon if lat == 0: _lat = True
# u,v = coordinates for the Thompson TM, Lee 54 elif lat == 0 and lon == self._1_e_90: # PYCHOK no cover u = self._iteration = self._zetaC = 0 v = self._Ev_cK else: # tau = tan(phi), taup = sinh(psi)
if lat != 90 else (lon, self.k0)
name=name or self.name)
'''(INTERNAL) Partial C{_zetaInv04} or C{_sigmaInv04}, Case 2 ''' # atan2(dlam-psi, psi+dlam) + 45d gives arg(zeta - zeta0) in # range [-135, 225). Subtracting 180 (multiplier is negative) # makes range [-315, 45). Multiplying by 1/3 (for cube root) # gives range [-105, 15). In particular the range [-90, 180] # in zeta space maps to [-90, 0] in w space as required. # Error using this guess is about 0.068 * rad^(5/3)
'''Get the most recent C{ExactTransverseMercator.forward} or C{ExactTransverseMercator.reverse} iteration number (C{int}) or C{None} if not available/applicable. '''
'''Get the central scale factor (C{float}), aka I{C{scale0}}. '''
'''Set the central scale factor (C{float}), aka I{C{scale0}}.
@raise ETMError: Invalid B{C{k0}}. ''' ExactTransverseMercator._k0_a._update(self) # redo ._k0_a self._k0 = k0
'''(INTERNAL) Get and cache C{k0 * equatoradius}. '''
'''Get the central meridian (C{degrees180}). '''
'''Set the central meridian (C{degrees180}).
@raise ETMError: Invalid B{C{lon0}}. '''
def majoradius(self): # PYCHOK no cover '''DEPRECATED, use property C{equatoradius}.''' return self.equatoradius
'''(INTERNAL) Get and cache C{_mu / 2 + 1}. ''' return _1_0 + self._mu * _0_5
'''(INTERNAL) Get and cache C{3 / _mv}. '''
'''(INTERNAL) Get and cache C{3 / (_mv * _e)}. '''
'''(INTERNAL) Invert C{_zetaInv2} or C{_sigmaInv2} using Newton's method.
@return: 2-Tuple C{(u, v)}.
@raise ETMError: No convergence. '''
else: # _sigmaInv2
# min iterations 2, max 6 or 7, mean 3.9 or 4.0
if r: # PYCHOK no cover i = callername(up=2, underOK=True) t = unstr(i, taup, lam, u, v, C=C) raise ETMError(Fmt.no_convergence(d2, tol2), txt=t)
@property_doc_(''' raise an L{ETMError} for convergence failures (C{bool}).''') def raiser(self): '''Get the error setting (C{bool}). ''' return self._raiser
'''Set the error setting (C{bool}), if C{True} throw an L{ETMError} for convergence failures. ''' self._raiser = bool(raiser)
'''(INTERNAL) Set the ellipsoid and elliptic moduli.
@arg datum: Ellipsoidal datum (C{Datum}).
@raise ETMError: Near-spherical B{C{datum}} or C{ellipsoid}. ''' or isnear0(mv, eps0=EPS02): # or sqrt(mu) != E.e raise ETMError(ellipsoid=E, txt=_near_(_spherical_))
_i = ExactTransverseMercator.iteration._uname _update_all(self, _i, '_sigmaC', '_zetaC')
'''Reverse projection, from Transverse Mercator to geographic.
@arg x: Easting of point (C{meters}). @arg y: Northing of point (C{meters}). @kwarg lon0: Central meridian (C{degrees180}), overriding the default if not C{None}. @kwarg name: Optional name (C{str}).
@return: L{Reverse4Tuple}C{(lat, lon, convergence, scale)}.
@see: C{void TMExact::Reverse(real lon0, real x, real y, real &lat, real &lon, real &gamma, real &k)}
@raise ETMError: No convergence, thrown if property C{raiser} set, ''' # undoes the steps in .forward. else: # enforce the parity if backside: # PYCHOK no cover xi = self._Eu_2cE_(xi)
# u,v = coordinates for the Thompson TM, Lee 54 else: # PYCHOK no cover u = self._iteration = self._sigmaC = 0 v = self._Ev_cK
else: # PYCHOK no cover g, k, lat, lon = _0_0, self.k0, _90_0, _0_0
if backside: # PYCHOK no cover lon, g = (_180_0 - lon), (_180_0 - g)
iteration=self._iteration, name=name or self.name)
'''(INTERNAL) C{scaled}.
@note: Argument B{C{d2}} is C{_mu * cnu**2 + _mv * cnv**2} from C{._zeta3}.
@return: 2-Tuple C{(convergence, scale)}.
@see: C{void TMExact::Scale(real tau, real /*lam*/, real snu, real cnu, real dnu, real snv, real cnv, real dnv, real &gamma, real &k)}. ''' # Lee 55.12 -- negated for our sign convention. g gives # the bearing (clockwise from true north) of grid north # Lee 55.13 with nu given by Lee 9.1 -- in sqrt change # the numerator from (1 - snu^2 * dnv^2) to (_mv * snv^2 # + cnu^2 * dnv^2) to maintain accuracy near phi = 90 # and change the denomintor from (dnu^2 + dnv^2 - 1) to # (_mu * cnu^2 + _mv * cnv^2) to maintain accuracy near # phi = 0, lam = 90 * (1 - e). Similarly rewrite sqrt in # 9.1 as _mv + _mu * c^2 instead of 1 - _mu * sin(phi)^2 # originally: sec2 = 1 + tau**2 # sec(phi)^2 # d2 = (mu * cnu**2 + mv * cnv**2) # q2 = (mv * snv**2 + cnudnv**2) / d2 # k = sqrt(mv + mu / sec2) * sqrt(sec2) * sqrt(q2) # = sqrt(mv * sec2 + mu) * sqrt(q2) # = sqrt(mv + mv * tau**2 + mu) * sqrt(q2) (k2 > 0 and q2 > 0) else _0_0 else: k = _OVERFLOW
'''(INTERNAL) C{sigma}.
@return: 3-Tuple C{(xi, eta, d2)}.
@see: C{void TMExact::sigma(real /*u*/, real snu, real cnu, real dnu, real v, real snv, real cnv, real dnv, real &xi, real &eta)}.
@raise ETMError: No convergence. ''' # Lee 55.4 writing # dnu^2 + dnv^2 - 1 = _mu * cnu^2 + _mv * cnv^2 else: mu, mv = map1(_overflow, mu, mv)
'''(INTERNAL) C{sigmaDwd}.
@return: 2-Tuple C{(du, dv)}.
@see: C{void TMExact::dwdsigma(real /*u*/, real snu, real cnu, real dnu, real /*v*/, real snv, real cnv, real dnv, real &du, real &dv)}. ''' # Reciprocal of 55.9: dw / ds = dn(w)^2/_mv, # expanding complex dn(w) using A+S 16.21.4
'''(INTERNAL) Invert C{sigma} using Newton's method.
@return: 2-Tuple C{(u, v)}.
@see: C{void TMExact::sigmainv(real xi, real eta, real &u, real &v)}.
@raise ETMError: No convergence. '''
'''(INTERNAL) Starting point for C{sigmaInv}.
@return: 4-Tuple C{(u, v, trip, Case)}.
@see: C{bool TMExact::sigmainv0(real xi, real eta, real &u, real &v)}. ''' # sigma as a simple pole at # w = w0 = Eu.K() + i * Ev.K() # and sigma is approximated by # sigma = (Eu.E() + i * Ev.KE()) + 1 / (w - w0)
# At w = w0 = i * Ev.K(), we have # sigma = sigma0 = i * Ev.KE() # sigma' = sigma'' = 0 # including the next term in the Taylor series gives: # sigma = sigma0 - _mv / 3 * (w - w0)^3 # When inverting this, we map arg(w - w0) = [-pi/2, -pi/6] # to arg(sigma - sigma0) = [-pi/2, pi/2] mapping arg = # [-pi/2, -pi/6] to [-pi/2, pi/2]
else: # use w = sigma * Eu.K/Eu.E (correct in limit _e -> 0)
'''(INTERNAL) Get 6-tuple C{(snu, cnu, dnu, snv, cnv, dnv)}. ''' # snu, cnu, dnu = self._Eu.sncndn(u) # snv, cnv, dnv = self._Ev.sncndn(v)
'''Return a C{str} representation.
@kwarg joined: Separator to join the attribute strings (C{str} or C{None} or C{NN} for non-joined). @kwarg kwds: Optional, overriding keyword arguments. ''' k0=self.k0, extendp=self.extendp)
'''(INTERNAL) C{zeta}.
@return: 3-Tuple C{(taup, lambda, d2)}.
@see: C{void TMExact::zeta(real /*u*/, real snu, real cnu, real dnu, real /*v*/, real snv, real cnv, real dnv, real &taup, real &lam)} ''' # Overflow value like atan(overflow) = pi/2 # Lee 54.17 but write # atanh(snu * dnv) = asinh(snu * dnv / sqrt(cnu^2 + _mv * snu^2 * snv^2)) # atanh(_e * snu / dnv) = asinh(_e * snu / sqrt(_mu * cnu^2 + _mv * cnv^2)) else: d1 = 0 else: d2 = 0 # psi = asinh(t1) - asinh(t2) # taup = sinh(psi) atan2(cnu * snv * e, dnu * cnv) * e) if d1 and d2 else _0_0
'''(INTERNAL) C{zetaDwd}.
@return: 2-Tuple C{(du, dv)}.
@see: C{void TMExact::dwdzeta(real /*u*/, real snu, real cnu, real dnu, real /*v*/, real snv, real cnv, real dnv, real &du, real &dv)}. ''' # Lee 54.21 but write (see A+S 16.21.4) # (1 - dnu^2 * snv^2) = (cnv^2 + _mu * snu^2 * snv^2)
'''(INTERNAL) Invert C{zeta} using Newton's method.
@return: 2-Tuple C{(u, v)}.
@see: C{void TMExact::zetainv(real taup, real lam, real &u, real &v)}.
@raise ETMError: No convergence. '''
'''(INTERNAL) Starting point for C{zetaInv}.
@return: 4-Tuple C{(u, v, trip, Case)}.
@see: C{bool TMExact::zetainv0(real psi, real lam, # radians real &u, real &v)}. ''' if psi < d and psi < self._e_PI_4_: # PYCHOK no cover # N.B. this branch is normally *not* taken because psi < 0 # is converted psi > 0 by .forward. There's a log singularity # at w = w0 = Eu.K() + i * Ev.K(), corresponding to the south # pole, where we have, approximately # psi = _e + i * pi/2 - _e * atanh(cos(i * (w - w0)/(1 + _mu/2))) # Inverting this gives: e = self._e # eccentricity s, c = sincos2((PI_2 - lam) / e) h, r = sinh(_1_0 - psi / e), self._1_mu_2 u = self._Eu_cK - r * asinh(s / hypot(c, h)) v = self._Ev_cK - r * atan2(c, h) return u, v, False, 1
# At w = w0 = i * Ev.K(), we have # zeta = zeta0 = i * (1 - _e) * pi/2 # zeta' = zeta'' = 0 # including the next term in the Taylor series gives: # zeta = zeta0 - (_mv * _e) / 3 * (w - w0)^3 # When inverting this, we map arg(w - w0) = [-90, 0] # to arg(zeta - zeta0) = [-90, 180]
# Use spherical TM, Lee 12.6 -- writing C{atanh(sin(lam) / # cosh(psi)) = asinh(sin(lam) / hypot(cos(lam), sinh(psi)))}. # This takes care of the log singularity at C{zeta = Eu.K()}, # corresponding to the north pole. # But scale to put 90, 0 on the right place
'''(INTERNAL) Recompute (T, L) from (u, v) to improve accuracy of Scale.
@arg sncndn6: 6-Tuple C{(snu, cnu, dnu, snv, cnv, dnv)}.
@return: 2-Tuple C{(g, k)} if not C{B{ll}} else 4-tuple C{(g, k, lat, lon)}. '''
'''Parse a string representing a UTM coordinate, consisting of C{"zone[band] hemisphere easting northing"}.
@arg strUTM: A UTM coordinate (C{str}). @kwarg datum: Optional datum to use (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @kwarg Etm: Optional class to return the UTM coordinate (L{Etm}) or C{None}. @kwarg falsed: Both easting and northing are C{falsed} (C{bool}). @kwarg name: Optional B{C{Etm}} name (C{str}).
@return: The UTM coordinate (B{C{Etm}}) or if B{C{Etm}} is C{None}, a L{UtmUps5Tuple}C{(zone, hemipole, easting, northing, band)}. The C{hemipole} is the hemisphere C{'N'|'S'}.
@raise ETMError: Invalid B{C{strUTM}}.
@raise TypeError: Invalid or near-spherical B{C{datum}}.
@example:
>>> u = parseETM5('31 N 448251 5411932') >>> u.toRepr() # [Z:31, H:N, E:448251, N:5411932] >>> u = parseETM5('31 N 448251.8 5411932.7') >>> u.toStr() # 31 N 448252 5411933 '''
name=NN, strict=True, zone=None, **cmoff): '''Convert a lat-/longitude point to an ETM 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 ETM coordinate, overriding B{C{latlon}}'s datum (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @kwarg Etm: Optional class to return the ETM coordinate (L{Etm}) or C{None}. @kwarg falsed: False both easting and northing (C{bool}). @kwarg name: Optional B{C{Utm}} name (C{str}). @kwarg strict: Restrict B{C{lat}} to UTM ranges (C{bool}). @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 ETM coordinate (B{C{Etm}}) or a L{UtmUps8Tuple}C{(zone, hemipole, easting, northing, band, datum, convergence, scale)} if B{C{Etm}} is C{None} or not B{C{falsed}}. The C{hemipole} is the C{'N'|'S'} hemisphere.
@raise ETMError: No convergence transforming to ETM east- and northing.
@raise ETMError: Invalid B{C{zone}} or near-spherical or incompatible B{C{datum}} or C{ellipsoid}.
@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{pygeodesy.rangerrors} set to C{True}.
@raise TypeError: Invalid or near-spherical B{C{datum}} or B{C{latlon}} not ellipsoidal.
@raise ValueError: The B{C{lon}} value is missing or B{C{latlon}} is invalid. ''' falsed, name, zone, strict, ETMError, **cmoff)
name, latlon, d.exactTM, Error=ETMError)
if __name__ == '__main__': # MCCABE 13
from sys import argv, exit as _exit
# mimick some of I{Karney}'s utility C{TransverseMercatorProj} _f = _r = _s = _t = False _as = argv[1:] while _as and _as[0].startswith(_DASH_): _a = _as.pop(0) if len(_a) < 2: _exit('%s: option %r invalid' % (_usage(*argv), _a)) elif '-forward'.startswith(_a): _f, _r = True, False elif '-reverse'.startswith(_a): _f, _r = False, True elif '-series'.startswith(_a): _s, _t = True, False elif _a == '-t': _s, _t = False, True elif '-help'.startswith(_a): _exit(_usage(argv[0], '[-s | -t]', '[-f[orward] <lat> <lon>', '| -r[everse] <easting> <northing>', '| <lat> <lon>]', '| -h[elp]')) else: _exit('%s: option %r not supported' % (_usage(*argv), _a)) if len(_as) > 1: f2 = map1(float, *_as[:2]) else: _exit('%s ...: incomplete' % (_usage(*argv),))
if _s: from pygeodesy.ktm import KTransverseMercator tm = KTransverseMercator() else: tm = ExactTransverseMercator(extendp=_t)
if _f: t = tm.forward(*f2) elif _r: t = tm.reverse(*f2) else: t = tm.forward(*f2) print(fstr(t, sep=_SPACE_)) t = tm.reverse(t.easting, t.northing) print(fstr(t, sep=_SPACE_))
# **) MIT License # # Copyright (C) 2016-2022 -- mrJean1 at Gmail -- All Rights Reserved. # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. |