Coverage for pygeodesy/epsg.py: 98%
101 statements
« prev ^ index » next coverage.py v7.2.2, created at 2024-05-06 16:50 -0400
« prev ^ index » next coverage.py v7.2.2, created at 2024-05-06 16:50 -0400
2# -*- coding: utf-8 -*-
4u'''European Petroleum Survey Group (EPSG) en-/decoding.
6Classes L{Epsg} and L{EPSGError} and functions to L{encode} and L{decode2}
7(U{EPSG<https://EPSG.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.
12A pure Python implementation transcoded from I{Charles Karney}'s C++ class U{UTMUPS
13<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1UTMUPS.html>},
14including coverage of UPS as zone C{0}.
15'''
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
30__all__ = _ALL_LAZY.epsg
31__version__ = '22.09.24'
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
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
43class Epsg(Int):
44 '''U{EPSG<https://EPSG.org>} class, a named C{int}.
45 '''
46 _band = NN
47 _epsg = None
48 _hemisphere = NN
49 _utmups = None
50 _zone = _UTMUPS_ZONE_INVALID
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.
56 @arg eisu: Other code (L{Epsg}, C{int}, C{str}, L{Utm} or L{Ups}).
58 @return: New L{Epsg}.
60 @raise TypeError: Invalid B{C{eisu}}.
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
74 elif isint(eisu):
75 self = int.__new__(cls, eisu)
76 self._epsg = eisu
77 self._zone, self._hemisphere = decode2(eisu) # PYCHOK UtmUps2Tuple
79 elif isstr(eisu):
80 self = encode(eisu)
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
90 if name:
91 self.name = name
92 return self
94 def __repr__(self):
95 return Fmt.PAREN(self.named, int.__repr__(self))
97 def __str__(self):
98 return int.__str__(self)
100 @Property_RO
101 def band(self):
102 '''Get the I{latitudinal} UTM or I{polar} UPS Band
103 (C{'A'|'B'|'C'|'D'|..|'W'|'X'|'Y'|'Z'} or C{""}).
104 '''
105 return self._band
107 @Property_RO
108 def hemisphere(self):
109 '''Get the UTM/UPS hemisphere/-pole (C{'N'|'S'}).
110 '''
111 return self._hemisphere
113 @Property_RO
114 def utmups(self):
115 '''Get the UTM/UPS original (L{Utm}, L{Ups}).
116 '''
117 return self._utmups
119 def utmupsStr(self, B=False):
120 '''Get the UTM/UPS zone, band and hemisphere/-pole (C{str}).
121 '''
122 b = self.band if B else NN
123 h = s = self.hemisphere
124 if h:
125 s = _SPACE_
126 return NN(Fmt.zone(self.zone), b, s, h)
128 @Property_RO
129 def zone(self):
130 '''Get the (longitudinal) UTM/UPS zone (C{int}, C{1..60} for UTM, C{0} for UPS).
131 '''
132 return self._zone
135class EPSGError(_ValueError):
136 '''EPSG encode, decode or other L{Epsg} issue.
137 '''
138 pass
141def decode2(epsg):
142 '''Determine the UTM/USP zone and hemisphere from a given
143 U{EPSG<https://EPSG.org>}.
145 @arg epsg: The EPSG (L{Epsg}, C{str} or C{scalar}).
147 @return: A L{UtmUps2Tuple}C{(zone, hemipole)}.
149 @raise EPSGError: Invalid B{C{epsg}}.
151 @note: Coverage of UPS as zone C{0} follows I{Karney}'s function U{UTMUPS::DecodeEPSG
152 <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1UTMUPS.html>}.
153 '''
154 if isinstance(epsg, Epsg):
155 z, h = epsg.zone, epsg.hemisphere
157 else:
158 try:
159 e = int(epsg) # int(long) OK
160 if _EPSG_N_01 <= e <= _EPSG_N_60:
161 z, h = int(e - _EPSG_N_01 + _UTM_ZONE_MIN), _N_
163 elif _EPSG_S_01 <= e <= _EPSG_S_60:
164 z, h = int(e - _EPSG_S_01 + _UTM_ZONE_MIN), _S_
166 elif e == _EPSG_N:
167 z, h = _UPS_ZONE, _N_
169 elif e == _EPSG_S:
170 z, h = _UPS_ZONE, _S_
172 else:
173 raise ValueError(NN)
174 except (TypeError, ValueError) as x:
175 raise EPSGError(epsg=epsg, cause=x)
177 return UtmUps2Tuple(z, h)
180def encode(zone, hemipole=NN, band=NN):
181 '''Determine the U{EPSG<https://EPSG.org>} code for
182 a given UTM/UPS zone number, hemisphere/pole and/or Band.
184 @arg zone: The (longitudinal) UTM zone (C{int}, 1..60) or UPS
185 zone (C{int}, 0) or UTM zone with/-out I{latitudinal}
186 Band letter (C{str}, '01C'..'60X') or UPS zone
187 with/-out I{polar} Band letter (C{str}, '00A', '00B',
188 '00Y' or '00Z').
189 @kwarg hemipole: UTM/UPS hemisphere or UPS projection top/center
190 pole (C{str}, C{'N[orth]'} or C{'S[outh]'}).
191 @kwarg band: Optional I{latitudinal} UTM or I{polar} UPS Band
192 letter (C{str}).
194 @return: C{EPSG} code (L{Epsg}).
196 @raise EPSGError: Invalid B{C{zone}}, B{C{hemipole}} or B{C{band}}.
198 @note: Coverage of UPS as zone C{0} follows I{Karney}'s function U{UTMUPS::EncodeEPSG
199 <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1UTMUPS.html>}.
200 '''
201 try:
202 z, B, hp = _to3zBhp(zone, band, hemipole=hemipole) # in .utmupsBase
203 if hp not in _NS_:
204 raise ValueError
205 except (TypeError, ValueError) as x:
206 raise EPSGError(zone=zone, hemipole=hemipole, band=band, cause=x)
208 if _UTM_ZONE_MIN <= z <= _UTM_ZONE_MAX:
209 e = z - _UTM_ZONE_MIN + (_EPSG_N_01 if hp == _N_ else _EPSG_S_01)
210 elif z == _UPS_ZONE:
211 e = _EPSG_N if hp == _N_ else _EPSG_S
212 else:
213 raise EPSGError(zone=zone)
215 e = Epsg(e)
216 e._band = B
217 # e._hemisphere = hp
218 return e
221__all__ += _ALL_OTHER(decode2, encode)
223# **) MIT License
224#
225# Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
226#
227# Permission is hereby granted, free of charge, to any person obtaining a
228# copy of this software and associated documentation files (the "Software"),
229# to deal in the Software without restriction, including without limitation
230# the rights to use, copy, modify, merge, publish, distribute, sublicense,
231# and/or sell copies of the Software, and to permit persons to whom the
232# Software is furnished to do so, subject to the following conditions:
233#
234# The above copyright notice and this permission notice shall be included
235# in all copies or substantial portions of the Software.
236#
237# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
238# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
239# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
240# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
241# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
242# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
243# OTHER DEALINGS IN THE SOFTWARE.