Coverage for pygeodesy/utm.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{Utm} and L{UTMError} and functions L{parseUTM5}, L{toUtm8} and L{utmZoneBand5}.
Pure Python implementation of UTM / WGS-84 conversion functions using an ellipsoidal earth model, transcoded from JavaScript originals by I{(C) Chris Veness 2011-2016} published under the same MIT Licence**, see U{UTM<https://www.Movable-Type.co.UK/scripts/latlong-utm-mgrs.html>} and U{Module utm<https://www.Movable-Type.co.UK/scripts/geodesy/docs/module-utm.html>}.
The U{UTM<https://WikiPedia.org/wiki/Universal_Transverse_Mercator_coordinate_system>} system is a 2-dimensional Cartesian coordinate system providing another way to identify locations on the surface of the earth. UTM is a set of 60 transverse Mercator projections, normally based on the WGS-84 ellipsoid. Within each zone, coordinates are represented as B{C{easting}}s and B{C{northing}}s, measured in metres.
This module includes some of I{Charles Karney}'s U{'Transverse Mercator with an accuracy of a few nanometers'<https://Arxiv.org/pdf/1002.1417v3.pdf>}, 2011 (building on Krüger's U{'Konforme Abbildung des Erdellipsoids in der Ebene' <https://bib.GFZ-Potsdam.DE/pub/digi/krueger2.pdf>}, 1912) and C++ class U{TransverseMercator<https://GeographicLib.SourceForge.io/C++/doc/ classGeographicLib_1_1TransverseMercator.html>}.
Some other references are U{Universal Transverse Mercator coordinate system <https://WikiPedia.org/wiki/Universal_Transverse_Mercator_coordinate_system>}, U{Transverse Mercator Projection<https://GeographicLib.SourceForge.io/tm.html>} and Henrik Seidel U{'Die Mathematik der Gauß-Krueger-Abbildung' <https://DE.WikiPedia.org/wiki/Gauß-Krüger-Koordinatensystem>}, 2006. '''
_xkwds_get _NS_, _outside_, _range_, _S_, _SPACE_, \ _UTM_, _V_, _X_, _zone_ # from pygeodesy.named import _xnamed # from .utmupsBase UtmUps8Tuple, UtmUpsLatLon5Tuple Property_RO _to3zBhp, _to3zll, _UPS_LATS, _UPS_ZONE, \ _UTM_LAT_MAX, _UTM_ZONE_MAX, \ _UTM_LAT_MIN, _UTM_ZONE_MIN, \ _UTM_ZONE_OFF_MAX, UtmUpsBase, _xnamed
degrees, radians, sin, sinh, tan, tanh
# I|O) 8° each, covering 80°S to 84°N and X repeated for 80-84°N
'''Universal Transverse Mercator (UTM parse or other L{Utm} issue. '''
'''(INTERNAL) Alpha or Beta Krüger series.
Krüger series summations for B{C{eta}}, B{C{ksi}}, B{C{p}} and B{C{q}}, caching the C{cos}, C{cosh}, C{sin} and C{sinh} values for the given B{C{eta}} and B{C{ksi}} angles (in C{radians}). ''' '''(INTERNAL) New Alpha or Beta Krüger series
@arg AB: Krüger Alpha or Beta series coefficients (C{4-, 6- or 8-tuple}). @arg x: Eta angle (C{radians}). @arg y: Ksi angle (C{radians}). '''
# assert len(self._ab) == len(self._pq) == n
# assert len(x2) == len(self._chx) == len(self._shx) == n
# self._sy, self._cy = splice(sincos2(*y2)) # PYCHOK false # assert len(y2) == len(self._cy) == len(self._sy) == n
'''(INTERNAL) Eta summation (C{float}). '''
'''(INTERNAL) Ksi summation (C{float}). '''
'''(INTERNAL) P summation (C{float}). '''
'''(INTERNAL) Q summation (C{float}). '''
'''(INTERNAL) Central meridian longitude (C{degrees180}). '''
'''(INTERNAL) False easting and northing. ''' # Karney, "Test data for the transverse Mercator projection (2009)" # <https://GeographicLib.SourceForge.io/C++/doc/transversemercator.html> # and <https://Zenodo.org/record/32470#.W4LEJS2ZON8>
'''(INTERNAL) Get the I{latitudinal} Band (row) letter. ''' raise E(lat=degDMS(lat), txt=t) else: return NN # None
'''(INTERNAL) Check and return zone, Band and band latitude.
@arg zone: Zone number or string. @arg band: Band letter. @arg Error: Exception to raise (L{UTMError}).
@return: 3-Tuple (zone, Band, latitude). ''' (_UPS_ZONE == z and Error is MGRSError)): raise Error(zone=zone)
except KeyError: raise Error(band=band or B, zone=z) else: # UTM raise Error(band=band or B, zone=z) raise Error(band=band, txt=MISSING)
'''(INTERNAL) Return zone, Band and lat- and (central) longitude in degrees.
@arg lat: Latitude (C{degrees}). @arg lon: Longitude (C{degrees}). @kwarg cmoff: Offset B{C{lon}} from zone's central meridian. @kwarg strict: Restrict B{C{lat}} to UTM ranges (C{bool}). @kwarg Error: Error for out of UTM range B{C{lat}}s.
@return: 4-Tuple (zone, Band, lat, lon). '''
t = _SPACE_(_outside_, _UTM_, _zone_, str(z), _by_, degDMS(x, prec=6)) raise Error(lon=degDMS(lon), txt=t)
'''(INTERNAL) Determine 7-tuple (zone, band, lat, lon, datum, falsed, name) for methods L{toEtm8} and L{toUtm8}. ''' raise Error(zone=zone)
'''Universal Transverse Mercator (UTM) coordinate. ''' # _band = NN # latitudinal band letter ('C'|..|'X', no 'I'|'O') # _scale = None # grid scale factor (C{scalar}) or C{None}
northing=0, band=NN, datum=_WGS84, falsed=True, convergence=None, scale=None, name=NN): '''New L{Utm} UTM coordinate.
@kwarg zone: Longitudinal UTM zone (C{int}, 1..60) or zone with/-out I{latitudinal} Band letter (C{str}, '1C'|..|'60X'). @kwarg hemisphere: Northern or southern hemisphere (C{str}, C{'N[orth]'} or C{'S[outh]'}). @kwarg easting: Easting, see B{C{falsed}} (C{meter}). @kwarg northing: Northing, see B{C{falsed}} (C{meter}). @kwarg band: Optional, I{latitudinal} band (C{str}, 'C'|..|'X', no 'I'|'O'). @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 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 TypeError: Invalid B{C{datum}}.
@raise UTMError: Invalid B{C{zone}}, B{C{hemishere}}, B{C{easting}}, B{C{northing}}, B{C{band}}, B{C{convergence}} or B{C{scale}}.
@example:
>>> import pygeodesy >>> u = pygeodesy.Utm(31, 'N', 448251, 5411932) '''
raise self._Error(hemisphere=hemisphere)
# if not falsed: # e, n = _false2(e, n, h) # # check easting/northing (with 40km overlap # # between zones) - is this worthwhile? # @raise RangeError: If B{C{easting}} or B{C{northing}} outside # the valid UTM range. # if 120e3 > e or e > 880e3: # raise RangeError(easting=easting) # if 0 > n or n > _FalseNorthing: # raise RangeError(northing=northing)
convergence=convergence, scale=scale)
return isinstance(other, Utm) and other.zone == self.zone \ and other.hemisphere == self.hemisphere \ and other.easting == self.easting \ and other.northing == self.northing \ and other.band == self.band \ and other.datum == self.datum
'''(INTERNAL) Make copy as an B{C{Xtm}} instance.
@arg Xtm: Class to return the copy (C{Xtm=Etm}, C{Xtm=Utm} or C{self.classof}). ''' self.easting, self.northing, band=self.band, datum=self.datum, falsed=self.falsed, scale=self.scale, convergence=self.convergence, name=name or self.name)
'''Get the I{latitudinal} band (C{'C'|..|'X'}). '''
'''Set or reset the I{latitudinal} band letter (C{'C'|..|'X'}) or C{None} or C{""} to reset.
@raise TypeError: Invalid B{C{band}}.
@raise ValueError: Invalid B{C{band}}. '''
'''(INTERNAL) Cache for method L{toEtm}. '''
'''Get the easting and northing falsing (L{EasNor2Tuple}C{(easting, northing)}). '''
'''Parse a string to a similar L{Utm} instance.
@arg strUTM: The UTM coordinate (C{str}), see function L{parseUTM5}. @kwarg name: Optional instance name (C{str}), overriding this name.
@return: The similar instance (L{Utm}).
@raise UTMError: Invalid B{C{strUTM}}.
@see: Functions L{pygeodesy.parseUPS5} and L{pygeodesy.parseUTMUPS5}. ''' return parseUTM5(strUTM, datum=self.datum, Utm=self.classof, name=name or self.name)
def parseUTM(self, strUTM): # PYCHOK no cover '''DEPRECATED, use method L{Utm.parse}.''' return self.parse(strUTM)
'''Get the top center of (stereographic) projection, C{""} always. ''' return NN # N/A for UTM
'''Copy this UTM to an ETM coordinate.
@return: The ETM coordinate (L{Etm}). '''
'''Convert this UTM coordinate to an (ellipsoidal) geodetic point.
@kwarg LatLon: Optional, ellipsoidal class to return the geodetic point (C{LatLon}) or C{None}. @kwarg eps: Optional convergence limit, L{EPS} or above (C{float}). @kwarg unfalse: Unfalse B{C{easting}} and B{C{northing}} if falsed (C{bool}). @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword arguments, ignored if C{B{LatLon} is None}.
@return: This UTM as (B{C{LatLon}}) or if B{C{LatLon}} is C{None}, as L{LatLonDatum5Tuple}C{(lat, lon, datum, convergence, scale)}.
@raise TypeError: Invalid B{C{datum}} or B{C{LatLon}} is not ellipsoidal.
@raise UTMError: Invalid meridional radius or H-value.
@example:
>>> u = Utm(31, 'N', 448251.795, 5411932.678) >>> from pygeodesy import ellipsoidalVincenty as eV >>> ll = u.toLatLon(eV.LatLon) # 48°51′29.52″N, 002°17′40.20″E '''
else:
'''(INTERNAL) Compute (ellipsoidal) lat- and longitude. '''
# from Karney 2011 Eq 15-22, 36 raise self._Error(meridional=A0)
raise self._Error(H=H)
# d may toggle on +/-1.12e-16 or +/-4.47e-16, # see the references at C{Ellipsoid.es_tauf} else: n = self.toLatLon.__name__ t = unstr(n, eps=eps, unfalse=unfalse) raise self._Error(Fmt.no_convergence(d), txt=t)
# convergence and scale: Karney 2011 Eq 26, 27 and 28
'''Return a string representation of this UTM coordinate.
Note that UTM coordinates are rounded, not truncated (unlike MGRS grid references).
@kwarg prec: Number of (decimal) digits, unstripped (C{int}). @kwarg fmt: Enclosing backets format (C{str}). @kwarg sep: Optional separator between name:value pairs (C{str}). @kwarg B: Optionally, include latitudinal band (C{bool}). @kwarg cs: Optionally, include meridian convergence and grid scale factor (C{bool} or non-zero C{int} to specify the precison like B{C{prec}}).
@return: This UTM as a string C{"[Z:09[band], H:N|S, E:meter, N:meter]"} plus C{", C:degrees, S:float"} if B{C{cs}} is C{True} (C{str}). '''
'''Return a string representation of this UTM coordinate.
To distinguish from MGRS grid zone designators, a space is left between the zone and the hemisphere.
Note that UTM coordinates are rounded, not truncated (unlike MGRS grid references).
@kwarg prec: Number of (decimal) digits, unstripped (C{int}). @kwarg sep: Optional separator to join (C{str}) or C{None} to return an unjoined C{tuple} of C{str}s. @kwarg B: Optionally, include latitudinal band (C{bool}). @kwarg cs: Optionally, include meridian convergence and grid scale factor (C{bool} or non-zero C{int} to specify the precison like B{C{prec}}).
@return: This UTM as a string with C{zone[band], hemisphere, easting, northing, [convergence, scale]} in C{"00 N|S meter meter"} plus C{" degrees float"} if B{C{cs}} is C{True} (C{str}).
@example:
>>> u = Utm(3, 'N', 448251, 5411932.0001) >>> u.toStr(4) # 03 N 448251.0 5411932.0001 >>> u.toStr(sep=', ') # 03 N, 448251, 5411932 '''
'''Convert this UTM coordinate to a UPS coordinate.
@kwarg pole: Optional top/center of the UPS projection, (C{str}, 'N[orth]'|'S[outh]'). @kwarg eps: Optional convergence limit, L{EPS} or above (C{float}), see method L{Utm.toLatLon}. @kwarg falsed: False both easting and northing (C{bool}).
@return: The UPS coordinate (L{Ups}). ''' name=self.name, pole=pole) return u
'''Convert this UTM coordinate to a different zone.
@arg zone: New UTM zone (C{int}). @kwarg eps: Optional convergence limit, L{EPS} or above (C{float}), see method L{Utm.toLatLon}. @kwarg falsed: False both easting and northing (C{bool}).
@return: The UTM coordinate (L{Utm}). ''' name=self.name, zone=zone) raise self._Error(zone=zone)
'''Get the (longitudinal) zone (C{int}, 1..60). '''
'''(INTERNAL) Parse a string representing a UTM coordinate, consisting of C{"zone[band] hemisphere easting northing"}, see L{pygeodesy.parseETM5} and L{parseUTM5}. ''' raise Error(strUTM=strUTM, zone=z, band=B)
else:
'''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 Utm: Optional class to return the UTM coordinate (L{Utm}) or C{None}. @kwarg falsed: Both easting and northing are falsed (C{bool}). @kwarg name: Optional B{C{Utm}} name (C{str}).
@return: The UTM coordinate (B{C{Utm}}) or if B{C{Utm}} is C{None}, a L{UtmUps5Tuple}C{(zone, hemipole, easting, northing, band)}. The C{hemipole} is the C{'N'|'S'} hemisphere.
@raise UTMError: Invalid B{C{strUTM}}.
@raise TypeError: Invalid B{C{datum}}.
@example:
>>> u = parseUTM5('31 N 448251 5411932') >>> u.toRepr() # [Z:31, H:N, E:448251, N:5411932] >>> u = parseUTM5('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 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 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 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{pygeodesy.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 ''' falsed, name, zone, strict, UTMError, **cmoff)
# easting, northing: Karney 2011 Eq 7-14, 29, 35
# convergence: Karney 2011 Eq 23, 24
# scale: Karney 2011 Eq 25
B, d, c, k, f, name, latlon, EPS)
name, latlon, eps, Error=UTMError): '''(INTERNAL) Helper for methods L{toEtm8} and L{toUtm8}. ''' else: convergence=c, scale=k), name) r._band = _toBand(lat)
'''Return the UTM zone number, Band letter, hemisphere and (clipped) lat- and longitude for a given location.
@arg lat: Latitude in degrees (C{scalar} or C{str}). @arg lon: Longitude in degrees (C{scalar} or C{str}). @kwarg cmoff: Offset longitude from the zone's central meridian (C{bool}). @kwarg name: Optional name (C{str}).
@return: A L{UtmUpsLatLon5Tuple}C{(zone, band, hemipole, lat, lon)} where C{hemipole} is the C{'N'|'S'} UTM 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{pygeodesy.rangerrors} set to C{True}.
@raise ValueError: Invalid B{C{lat}} or B{C{lon}}. '''
# **) 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. |