Coverage for pygeodesy/mgrs.py : 96%

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 -*-
u'''Military Grid Reference System (MGRS/NATO) references.
Military Grid Reference System (MGRS/NATO) classes L{Mgrs} and L{MGRSError} and functions L{parseMGRS} and L{toMgrs}.
Pure Python implementation of MGRS / UTM / UPS conversion functions using an ellipsoidal earth model, in part transcoded from JavaScript originals by I{(C) Chris Veness 2014-2016} published under the same MIT Licence**, see U{MGRS<https://www.Movable-Type.co.UK/scripts/latlong-utm-mgrs.html>} and U{Module mgrs<https://www.Movable-Type.co.UK/scripts/geodesy/docs/module-mgrs.html>}.
The MGRS/NATO grid references provide geocoordinate references covering the entire globe, based on UTM and UPS projections.
MGRS references comprise a grid zone designation (GZD), a 100 km grid (square) tile identification, and an easting and northing (in C{meter}). The GZD consists of a longitudinal zone (or column) number and latitudinal band (row) letter. Each zone (column) is 6 degrees wide and each band (row) is 8 degrees high, except the top one 'X' is 12 degrees tall.
See also the U{United States National Grid<https://www.FGDC.gov/standards/projects/ FGDC-standards-projects/usng/fgdc_std_011_2001_usng.pdf>} and U{Military Grid Reference System<https://WikiPedia.org/wiki/Military_grid_reference_system>}. '''
_ValueError, _xkwds _datum_, _easting_, _northing_, _not_, _SPACE_, \ _splituple, _W_, _Y_, _Z_, _zone_, _0_, \ _0_5, _N_90_0 _UTM_ZONE_MAX, _UTM_ZONE_MIN
# <https://GitHub.com/hrbrmstr/mgrs/blob/master/src/mgrs.c> _B_: _N_90_0, _Z_: _UTM_LAT_MAX} # PYCHOK see .utm._to3zBlat # 100 km grid tile UTM column (E) letters, repeating every third zone # 100 km grid tile UPS column (E) letters for each polar zone # 100 km grid tile UTM and UPS row (N) letters, repeating every other zone
'''(INTERNAL) Lazily compiled regex-es. '''
'''Military Grid Reference System (MGRS/NATO) references, with method to convert to UTM coordinates. '''
datum=_WGS84, resolution=0, 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 tile using I{only} the I{AA} or I{MGRS-New} (row) U{lettering scheme <http://Wikipedia.org/wiki/Military_Grid_Reference_System>}. @arg easting: Easting (C{meter}), within 100 km grid tile. @arg northing: Northing (C{meter}), within 100 km grid tile. @kwarg band: Optional, 8° I{latitudinal} band (C{str}), 'C'|..|'W'|'X' covering 80°S..84°N (without 'I' and 'O') or I{polar} region 'A'|'B' at the south or 'Y'|'Z' at the north pole. @kwarg datum: Optional this reference's datum (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @kwarg resolution: Optional resolution, cell size (C{meter}) or C{0}. @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 ''' self.name = name
raise IndexError # caught below except (IndexError, TypeError, ValueError): raise MGRSError(en100k=en100k, zone=zone)
self._datum = _ellipsoidal_datum(datum, name=name)
'''Get the I{latitudinal} C{'C'|..|'W'|'X'} or I{polar} C{'A'|'B'|'Y'|'Z'}) band letter (C{str}). '''
'''Get the band latitude (C{degrees90}). ''' else: # must have been set raise _AssertionError(band=self.band, Lat=a)
'''Get the datum (L{Datum}). '''
'''Get the 2-character grid EN digraph (C{str}). '''
def en100k(self): # PYCHOK no cover '''DEPRECATED, use property C{EN}.''' return self.EN
'''(INTERNAL) Convert grid letters to east-/northing meter. ''' # get easting from E column (note, +1 because # eastings start at 166e3 due to 500 km false origin) # similarly, get northing from N row else: raise _AssertionError(zone=self.zone)
'''Gets the easting (C{meter} within grid tile). '''
'''Get easting and northing (L{EasNor2Tuple}C{(easting, northing)}). ''' return EasNor2Tuple(self.easting, self.northing)
'''Is this MGRS in a (polar) UPS zone (C{bool}). '''
'''Is this MGRS in a (non-polar) UTM zone (C{bool}). '''
'''Get the northing (C{meter} within grid tile). '''
'''Get northing of the band bottom (C{meter}), extended to include entirety of bottom-most 100 km tile. ''' toUps8(a, 0, datum=self.datum, Ups=None)
'''Parse a string to a similar L{Mgrs} instance.
@arg strMGRS: The MGRS reference (C{str}), see function L{parseMGRS}. @kwarg name: Optional instance name (C{str}), overriding this name.
@return: The similar instance (L{Mgrs}).
@raise MGRSError: Invalid B{C{strMGRS}}. ''' name=name or self.name)
'''Get the resolution (C{meter}). '''
'''Set the resolution of this L{Mgrs} instance, as cell size (C{meter}, power of 10).
@raise MGRSError: Invalid B{C{resolution}}. ''' raise ValueError else: r = 0 except (ValueError, TypeError): raise MGRSError(resolution=resolution)
'''Get the MGRS grid tile size (C{meter}). ''' assert _MODS.utmups._MGRS_TILE is _100km return _100km
'''Convert this MGRS grid reference to a UTM coordinate.
@kwarg LatLon: Optional, ellipsoidal class to return the geodetic point (C{LatLon}) or C{None}. @kwarg center: Optionally, return the grid's center or lower left corner (C{bool}). @kwarg toLatLon_kwds: Optional, additional L{Utm.toLatLon} and B{C{LatLon}} keyword arguments.
@return: A B{C{LatLon}} instance or if C{B{LatLon} is None} a L{LatLonDatum5Tuple}C{(lat, lon, datum, convergence, scale)}.
@raise TypeError: If B{C{LatLon}} is not ellipsoidal.
@raise UTMError: Invalid meridional radius or H-value.
@see: Methods L{Mgrs.toUtm} and L{Utm.toLatLon}. '''
'''Return a string representation of this MGRS grid reference.
@kwarg prec: Number of digits (C{int}), 4:Km, 10:m. @kwarg fmt: Enclosing backets format (C{str}). @kwarg sep: Separator between name:values (C{str}). @return: This Mgrs as "[Z:00B, G:EN, E:meter, N:meter]" (C{str}). '''
'''Return a string representation of this MGRS grid reference.
Note that MGRS grid references are truncated, not rounded (unlike UTM coordinates).
@kwarg prec: Number of digits (C{int}), 4:Km, 10:m. @kwarg sep: Optional separator to join (C{str}) or C{None} to return an unjoined C{tuple} of C{str}s.
@return: This Mgrs as "00B EN easting northing" (C{str}).
@raise ValueError: Invalid B{C{prec}}.
@example:
>>> m = Mgrs(31, 'DQ', 48251, 11932, band='U') >>> m.toStr() # '31U DQ 48251 11932' '''
'''Convert this MGRS grid reference to a UPS coordinate.
@kwarg Ups: Optional class to return the UPS coordinate (L{Ups}) or C{None}. @kwarg center: Optionally, center easting and northing by the resolution (C{bool}).
@return: A B{C{Ups}} instance or if C{B{Ups} is None} a L{UtmUps5Tuple}C{(zone, hemipole, easting, northing, band)}.
@raise MGRSError: This MGRS is a I{non-polar} UTM reference. ''' if self.isUTM: raise MGRSError(zoneB=self.zoneB, txt=_not_(_polar_)) return self._toUtmUps(Ups, center)
'''Convert this MGRS grid reference to a UTM coordinate.
@kwarg Utm: Optional class to return the UTM coordinate (L{Utm}) or C{None}. @kwarg center: Optionally, center easting and northing by the resolution (C{bool}).
@return: A B{C{Utm}} instance or if C{B{Utm} is None} a L{UtmUps5Tuple}C{(zone, hemipole, easting, northing, band)}.
@raise MGRSError: This MGRS is a I{polar} UPS reference. ''' raise MGRSError(zoneB=self.zoneB, txt=_polar_)
'''Convert this MGRS grid reference to a UTM or UPS coordinate.
@kwarg Utm: Optional class to return the UTM coordinate (L{Utm}) or C{None}. @kwarg Ups: Optional class to return the UPS coordinate (L{Utm}) or C{None}. @kwarg center: Optionally, center easting and northing by the resolution (C{bool}).
@return: A B{C{Utm}} or B{C{Ups}} instance or if C{B{Utm} or B{Ups} is None} a L{UtmUps5Tuple}C{(zone, hemipole, easting, northing, band)}. ''' (Ups if self.isUPS else None)), center)
'''(INTERNAL) Helper for C{.toUps} and C{.toUtm}. ''' # 100 km grid tile row letters repeat every 2,000 km north; # add enough 2,000 km blocks to get into required band else U(z, h, e, n, B, name=m, datum=self.datum)
'''Get the I{longitudinal} zone (C{int}, 1..60 or 0 for polar). '''
'''Get the I{longitudinal} zone digits and I{latitudinal} band letter (C{str}). '''
'''4-Tuple C{(zone, digraph, easting, northing)}, C{zone} and C{digraph} as C{str}, C{easting} and C{northing} in C{meter}. '''
'''Extend this L{Mgrs4Tuple} to a L{Mgrs6Tuple}.
@arg band: The band to add (C{str} or C{None}). @arg datum: The datum to add (L{Datum} or C{None}).
@return: An L{Mgrs6Tuple}C{(zone, digraph, easting, northing, band, datum)}. '''
'''6-Tuple C{(zone, digraph, easting, northing, band, datum)}, C{zone}, C{digraph} and C{band} as C{str}, C{easting} and C{northing} in C{meter} and C{datum} a L{Datum}. '''
'''Parse a string representing a MGRS grid reference, consisting of C{"zoneBand, grid, easting, northing"}.
@arg strMGRS: MGRS grid reference (C{str}). @kwarg datum: Optional datum to use (L{Datum}). @kwarg Mgrs: Optional class to return the MGRS grid reference (L{Mgrs}) or C{None}. @kwarg name: Optional B{C{Mgrs}} name (C{str}).
@return: The MGRS grid reference as B{C{Mgrs}} or if C{B{Mgrs} is None} as an L{Mgrs4Tuple}C{(zone, digraph, easting, northing)}.
@raise MGRSError: Invalid B{C{strMGRS}}.
@example:
>>> m = parseMGRS('31U DQ 48251 11932') >>> str(m) # '31U DQ 48251 11932' >>> m = parseMGRS('31UDQ4825111932') >>> repr(m) # [Z:31U, G:DQ, E:48251, N:11932] >>> m = mgrs.parseMGRS('42SXD0970538646') >>> str(m) # '42S XD 09705 38646' >>> m = mgrs.parseMGRS('42SXD9738') # Km >>> str(m) # '42S XD 97000 38000' >>> m = mgrs.parseMGRS('YUB17770380') # polar >>> str(m) # '00Y UB 17770 03800' ''' raise ValueError
# convert to meter if less than 5 digits
raise ValueError
r = Mgrs4Tuple(zB, EN, e, n, name=name) else:
strMGRS=strMGRS, Error=MGRSError)
'''Convert a UTM or UPS coordinate to an MGRS grid reference.
@arg utmups: A UTM or UPS coordinate (L{Utm}, L{Etm} or L{Ups]). @kwarg Mgrs: Optional class to return the MGRS grid reference (L{Mgrs}) or C{None}. @kwarg name: Optional B{C{Mgrs}} name (C{str}). @kwarg Mgrs_kwds: Optional, additional B{C{Mgrs}} keyword arguments, ignored if C{B{Mgrs} is None}.
@return: The MGRS grid reference as B{C{Mgrs}} or if C{B{Mgrs} is None} as an L{Mgrs6Tuple}C{(zone, digraph, easting, northing, band, datum)}.
@raise MGRSError: Invalid B{C{utmups}}.
@raise TypeError: If B{C{utmups}} is not L{Utm} nor L{Etm} nor L{Ups}.
@example:
>>> u = Utm(31, 'N', 448251, 5411932) >>> m = u.toMgrs() # 31U DQ 48251 11932 ''' # _MODS.utmups.utmupsValidate(utmups, MGRS=True, Error-MGRSError) # columns in zone 1 are A-H, zone 2 J-R, zone 3 S-Z, # then repeating every 3rd zone (note E-1 because # eastings start at 166e3 due to 500km false origin) # rows in even zones are A-V, in odd zones are F-E else: raise _ValueError(zone=z) except (IndexError, TypeError, ValueError) as x: raise MGRSError(B=B, E=E, N=N, utmups=utmups, txt=str(x))
else:
'''(INTERNAL) An MGRS east-/northing truncated to micrometer (um) precision and to grid tile C{M} and C{m}eter within the tile. '''
if __name__ == '__main__':
from pygeodesy.ellipsoidalVincenty import LatLon from pygeodesy.lazily import printf
e = n = 0 for lat in range(-89, 90, 1): for lon in range(-173, 180, 1): m = LatLon(lat, lon).toMgrs() t = m.toLatLon(LatLon=LatLon) d = max(abs(t.lat - lat), abs(t.lon - lon)) if d > 1e-9: e += 1 printf('%s: %s %s vs %s %.6e', e, m, t.latlon, (float(lat), float(lon)), d) n += 1 p = e * 100.0 / n printf('%s/%s failures (%.2f%%)', (e if e else 'no'), n, p)
# **) 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. |