1 2# -*- coding: utf-8 -*- 3 4u'''European Petroleum Survey Group (EPSG) en-/decoding. 5 6Classes L{Epsg} and L{EPSGError} and functions to L{encode} and L{decode2} 7(U{EPSG<https://www.EPSG-Registry.org>}) codes from and to U{UTM 8<https://WikiPedia.org/wiki/Universal_Transverse_Mercator_coordinate_system>} and 9U{UPS<https://WikiPedia.org/wiki/Universal_polar_stereographic_coordinate_system>} 10zones. 11 12A pure Python implementation transcoded from I{Charles Karney}'s C++ class 13U{UTMUPS<https://GeographicLib.SourceForge.io/html/classGeographicLib_1_1UTMUPS.html>}, 14including coverage of UPS as zone C{0}. 15''' 16 17from pygeodesy.basics import isint, isstr, _xinstanceof 18from pygeodesy.errors import _ValueError 19from pygeodesy.interns import NN, _N_, _NS_, _S_, _SPACE_ 20from pygeodesy.lazily import _ALL_LAZY, _ALL_OTHER 21from pygeodesy.namedTuples import UtmUps2Tuple 22from pygeodesy.props import Property_RO 23from pygeodesy.streprs import Fmt 24from pygeodesy.units import Int 25from pygeodesy.ups import Ups 26from pygeodesy.utm import Utm 27from pygeodesy.utmupsBase import _to3zBhp, _UPS_ZONE, _UTM_ZONE_MIN, \ 28 _UTM_ZONE_MAX, _UTMUPS_ZONE_INVALID 29 30__all__ = _ALL_LAZY.epsg 31__version__ = '21.06.09' 32 33# _EPSG_INVALID = _UTMUPS_ZONE_INVALID 34_EPSG_N_01 = 32601 # EPSG code for UTM zone 01 N 35_EPSG_N_60 = 32660 # EPSG code for UTM zone 60 N 36_EPSG_N = 32661 # EPSG code for UPS pole N 37 38_EPSG_S_01 = 32701 # EPSG code for UTM zone 01 S 39_EPSG_S_60 = 32760 # EPSG code for UTM zone 60 S 40_EPSG_S = 32761 # EPSG code for UPS pole S 41 42 43class Epsg(Int): 44 '''U{EPSG<https://www.EPSG-Registry.org>} class, a named C{int}. 45 ''' 46 _band = NN 47 _epsg = None 48 _hemisphere = NN 49 _utmups = None 50 _zone = _UTMUPS_ZONE_INVALID 51 52 def __new__(cls, eisu, name=NN): 53 '''New L{Epsg} (I{European Petroleum Survey Group}) code from a 54 UTM/USP coordinate or other EPSG code. 55 56 @arg eisu: Other code (L{Epsg}, C{int}, C{str}, L{Utm} or L{Ups}). 57 58 @return: New L{Epsg}. 59 60 @raise TypeError: Invalid B{C{eisu}}. 61 62 @raise EPSGError: Invalid B{C{eisu}}. 63 ''' 64 if isinstance(eisu, Epsg): 65 self = int.__new__(cls, int(eisu)) 66 self._band = eisu.band 67 self._epsg = self # XXX eisu 68 self._hemisphere = eisu.hemisphere 69 self._utmups = eisu.utmups 70 self._zone = eisu.zone 71 if eisu.name: 72 self.name = eisu.name 73 74 elif isint(eisu): 75 self = int.__new__(cls, eisu) 76 self._epsg = eisu 77 self._zone, self._hemisphere = decode2(eisu) # PYCHOK UtmUps2Tuple 78 79 elif isstr(eisu): 80 self = encode(eisu) 81 82 else: 83 u = eisu 84 _xinstanceof(Utm, Ups, eisu=u) 85 self = encode(u.zone, hemipole=u.hemisphere, band=u.band) # PYCHOK **kwds 86 self._utmups = u 87 if u.name: 88 self.name = u.name 89 90 if name: 91 self.name = name 92 return self 93 94 def __repr__(self): 95 return Fmt.PAREN(self.named, int.__repr__(self)) 96 97 def __str__(self): 98 return int.__str__(self) 99 100 @Property_RO 101 def band(self): 102 '''Get the (latitudinal) UTM/UPS Band (C{'A'|'B'|'C'|'D'..'W'|'X'|'Y'|'Z'} or C{""}). 103 ''' 104 return self._band 105 106 @Property_RO 107 def hemisphere(self): 108 '''Get the UTM/UPS hemisphere/-pole (C{'N'|'S'}). 109 ''' 110 return self._hemisphere 111 112 @Property_RO 113 def utmups(self): 114 '''Get the UTM/UPS original (L{Utm}, L{Ups}). 115 ''' 116 return self._utmups 117 118 def utmupsStr(self, B=False): 119 '''Get the UTM/UPS zone, band and hemisphere/-pole (C{str}). 120 ''' 121 b = self.band if B else NN 122 h = s = self.hemisphere 123 if h: 124 s = _SPACE_ 125 return NN(Fmt.zone(self.zone), b, s, h) 126 127 @Property_RO 128 def zone(self): 129 '''Get the (longitudinal) UTM/UPS zone (C{int}, C{1..60} for UTM, C{0} for UPS). 130 ''' 131 return self._zone 132 133 134class EPSGError(_ValueError): 135 '''EPSG encode, decode or other L{Epsg} issue. 136 ''' 137 pass 138 139 140def decode2(epsg): 141 '''Determine the UTM/USP zone and hemisphere from a given 142 U{EPSG<https://www.EPSG-Registry.org>}. 143 144 @arg epsg: The EPSG (L{Epsg}, C{str} or C{scalar}). 145 146 @return: A L{UtmUps2Tuple}C{(zone, hemipole)}. 147 148 @raise EPSGError: Invalid B{C{epsg}}. 149 150 @note: Coverage of UPS as zone C{0} follows I{Karney}'s function U{UTMUPS::DecodeEPSG 151 <https://GeographicLib.SourceForge.io/html/classGeographicLib_1_1UTMUPS.html>}. 152 ''' 153 if isinstance(epsg, Epsg): 154 z, h = epsg.zone, epsg.hemisphere 155 156 else: 157 try: 158 e = int(epsg) # int(long) OK 159 if _EPSG_N_01 <= e <= _EPSG_N_60: 160 z, h = int(e - _EPSG_N_01 + _UTM_ZONE_MIN), _N_ 161 162 elif _EPSG_S_01 <= e <= _EPSG_S_60: 163 z, h = int(e - _EPSG_S_01 + _UTM_ZONE_MIN), _S_ 164 165 elif e == _EPSG_N: 166 z, h = _UPS_ZONE, _N_ 167 168 elif e == _EPSG_S: 169 z, h = _UPS_ZONE, _S_ 170 171 else: 172 raise ValueError 173 except (TypeError, ValueError) as x: 174 raise EPSGError(epsg=epsg, txt=str(x)) 175 176 return UtmUps2Tuple(z, h) 177 178 179def encode(zone, hemipole=NN, band=NN): 180 '''Determine the U{EPSG<https://www.EPSG-Registry.org>} code for 181 a given UTM/UPS zone number, hemisphere/pole and/or Band. 182 183 @arg zone: The (longitudinal) UTM zone (C{int}, 1..60) or UPS 184 zone (C{int}, 0) or UTM zone with/-out (latitudinal) 185 Band letter (C{str}, '01C'..'60X') or UPS zone 186 with/-out (polar) Band letter (C{str}, '00A', '00B', 187 '00Y' or '00Z'). 188 @kwarg hemipole: UTM/UPS hemisphere or UPS projection top/center 189 pole (C{str}, C{'N[orth]'} or C{'S[outh]'}). 190 @kwarg band: Optional (latitudinal) UTM or (polar) UPS Band 191 letter (C{str}). 192 193 @return: C{EPSG} code (L{Epsg}). 194 195 @raise EPSGError: Invalid B{C{zone}}, B{C{hemipole}} or B{C{band}}. 196 197 @note: Coverage of UPS as zone C{0} follows I{Karney}'s function U{UTMUPS::EncodeEPSG 198 <https://GeographicLib.SourceForge.io/html/classGeographicLib_1_1UTMUPS.html>}. 199 ''' 200 try: 201 z, B, hp = _to3zBhp(zone, band, hemipole=hemipole) # in .ellipsoidalBase 202 if hp not in _NS_: 203 raise ValueError 204 except (TypeError, ValueError) as x: 205 raise EPSGError(zone=zone, hemipole=hemipole, band=band, txt=str(x)) 206 207 if _UTM_ZONE_MIN <= z <= _UTM_ZONE_MAX: 208 e = z - _UTM_ZONE_MIN + (_EPSG_N_01 if hp == _N_ else _EPSG_S_01) 209 elif z == _UPS_ZONE: 210 e = _EPSG_N if hp == _N_ else _EPSG_S 211 else: 212 raise EPSGError(zone=zone) 213 214 e = Epsg(e) 215 e._band = B 216 # e._hemisphere = hp 217 return e 218 219 220__all__ += _ALL_OTHER(decode2, encode) 221 222# **) MIT License 223# 224# Copyright (C) 2016-2021 -- mrJean1 at Gmail -- All Rights Reserved. 225# 226# Permission is hereby granted, free of charge, to any person obtaining a 227# copy of this software and associated documentation files (the "Software"), 228# to deal in the Software without restriction, including without limitation 229# the rights to use, copy, modify, merge, publish, distribute, sublicense, 230# and/or sell copies of the Software, and to permit persons to whom the 231# Software is furnished to do so, subject to the following conditions: 232# 233# The above copyright notice and this permission notice shall be included 234# in all copies or substantial portions of the Software. 235# 236# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 237# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 238# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 239# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 240# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 241# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 242# OTHER DEALINGS IN THE SOFTWARE. 243