Hide keyboard shortcuts

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

 

# -*- coding: utf-8 -*- 

 

u'''Functions L{parseUTMUPS5}, L{toUtmUps8}, L{UtmUps} and 

L{utmupsZoneBand5} to handle both I{Universal Transverse Mercator 

(U{UTM<https://WikiPedia.org/wiki/Universal_Transverse_Mercator_coordinate_system>})} 

and I{Universal Polar Stereographic 

(U{UPS<https://WikiPedia.org/wiki/Universal_polar_stereographic_coordinate_system>})} 

coordinates. 

 

A pure Python implementation, partially transcribed from C++ class U{UTMUPS 

<https://GeographicLib.SourceForge.io/html/classGeographicLib_1_1UTMUPS.html>} 

by I{Charles Karney}. 

''' 

 

from pygeodesy.datum import Datums 

from pygeodesy.dms import RangeError 

from pygeodesy.lazily import _ALL_LAZY 

from pygeodesy.named import UtmUps5Tuple, UtmUps8Tuple 

from pygeodesy.utily import OK 

from pygeodesy.ups import parseUPS5, toUps8, Ups, UPSError, upsZoneBand5 

from pygeodesy.utm import parseUTM5, toUtm8, Utm, UTMError, utmZoneBand5 

from pygeodesy.utmupsBase import _MGRS_TILE, _to4lldn, _to3zBhp, \ 

_UPS_ZONE, _UPS_ZONE_STR, \ 

_UTMUPS_ZONE_MIN, _UTMUPS_ZONE_MAX 

 

# all public contants, classes and functions 

__all__ = _ALL_LAZY.utmups 

__version__ = '19.07.12' 

 

_UPS_N_MAX = 27 * _MGRS_TILE 

_UPS_N_MIN = 13 * _MGRS_TILE 

_UPS_S_MAX = 32 * _MGRS_TILE 

_UPS_S_MIN = 8 * _MGRS_TILE 

 

_UTM_C_MAX = 9 * _MGRS_TILE 

_UTM_C_MIN = 1 * _MGRS_TILE 

_UTM_N_MAX = 95 * _MGRS_TILE 

_UTM_N_MIN = 0 * _MGRS_TILE 

_UTM_S_MAX = 100 * _MGRS_TILE 

_UTM_S_MIN = 10 * _MGRS_TILE 

 

_UTM_N_SHIFT = _UTM_S_MAX - _UTM_N_MIN # South minus North UTM northing 

 

 

class _UpsMinMax(object): 

# UPS ranges for South, North pole 

eMax = _UPS_S_MAX, _UPS_N_MAX 

eMin = _UPS_S_MIN, _UPS_N_MIN 

nMax = _UPS_S_MAX, _UPS_N_MAX 

nMin = _UPS_S_MIN, _UPS_N_MIN 

 

 

class _UtmMinMax(object): 

# UTM ranges for South-, Northern hemisphere 

eMax = _UTM_C_MAX, _UTM_C_MAX 

eMin = _UTM_C_MIN, _UTM_C_MIN 

nMax = (_UTM_N_MAX + _UTM_N_SHIFT), _UTM_N_MAX 

nMin = _UTM_S_MIN, (_UTM_S_MIN - _UTM_N_SHIFT) 

 

 

class UTMUPSError(ValueError): 

'''Universal Transverse Mercator/Universal Polar Stereographic 

(UTM/UPS) parse, validate or other issue. 

''' 

pass 

 

 

def parseUTMUPS5(strUTMUPS, datum=Datums.WGS84, Utm=Utm, Ups=Ups, name=''): 

'''Parse a string representing a UTM or UPS coordinate, consisting 

of C{"zone[band] hemisphere/pole easting northing"}. 

 

@param strUTMUPS: A UTM or UPS coordinate (C{str}). 

@keyword datum: Optional datum to use (L{Datum}). 

@keyword Utm: Optional (sub-)class to return the UTM coordinate 

(L{Utm}) or C{None}. 

@keyword Ups: Optional (sub-)class to return the UPS coordinate 

(L{Ups}) or C{None}. 

@keyword name: Optional name (C{str}). 

 

@return: The UTM or UPS coordinate (B{C{Utm}} or B{C{Ups}}) or 

a L{UtmUps5Tuple}C{(zone, 

hemipole, easting, northing, band)} if B{C{Utm}} 

respectively B{C{Ups}} or both are C{None}. The 

C{hemipole} is C{'N'|'S'}, the UTM hemisphere 

or UPS pole, the UPS projection top/center. 

 

@raise UTMUPSError: Invalid B{C{strUTMUPS}}. 

 

@see: Functions L{parseUTM5} and L{parseUPS5}. 

''' 

try: 

try: 

u = parseUTM5(strUTMUPS, datum=datum, Utm=Utm, name=name) 

except UTMError: 

u = parseUPS5(strUTMUPS, datum=datum, Ups=Ups, name=name) 

except (UTMError, UPSError): 

raise UTMUPSError('%s invalid: %r' % ('strUTMUPS', strUTMUPS)) 

return u 

 

 

def toUtmUps8(latlon, lon=None, datum=None, falsed=True, Utm=Utm, Ups=Ups, 

pole='', name='', **cmoff): 

'''Convert a lat-/longitude point to a UTM or UPS coordinate. 

 

@param latlon: Latitude (C{degrees}) or an (ellipsoidal) 

geodetic C{LatLon} point. 

@keyword lon: Optional longitude (C{degrees}) or C{None}. 

@keyword datum: Optional datum to use this UTM coordinate, 

overriding B{C{latlon}}'s datum (C{Datum}). 

@keyword falsed: False both easting and northing (C{bool}). 

@keyword Utm: Optional (sub-)class to return the UTM coordinate 

(L{Utm}) or C{None}. 

@keyword Ups: Optional (sub-)class to return the UPS coordinate 

(L{Ups}) or C{None}. 

@keyword pole: Optional top/center of UPS (stereographic) 

projection (C{str}, C{'N[orth]'} or C{'S[outh]'}). 

@keyword name: Optional name (C{str}). 

@keyword cmoff: DEPRECATED, use B{C{falsed}}. Offset longitude 

from zone's central meridian, for UTM only (C{bool}). 

 

@return: The UTM or UPS coordinate (B{C{Utm}} respectively B{C{Ups}}) 

or a L{UtmUps8Tuple}C{(zone, 

hemipole, easting, northing, band, datum, convergence, 

scale)} if B{C{Utm}} respectively B{C{Ups}} is C{None} or 

B{C{cmoff}} is C{False}. 

 

@raise RangeError: If B{C{lat}} outside the valid UTM or UPS bands 

or if B{C{lat}} or B{C{lon}} outside the valid 

range and L{rangerrors} set to C{True}. 

 

@raise TypeError: If B{C{latlon}} is not ellipsoidal or B{C{lon}} 

value is missing. 

 

@raise UTMUPSError: UTM or UPS validation failed. 

 

@raise ValueError: Invalid B{C{lat}} or B{C{lon}}. 

 

@see: Functions L{toUtm8} and L{toUps8}. 

''' 

lat, lon, d, name = _to4lldn(latlon, lon, datum, name) 

z, B, p, lat, lon = utmupsZoneBand5(lat, lon) 

 

f = falsed and cmoff.get('cmoff', True) 

if z == _UPS_ZONE: 

u = toUps8(lat, lon, datum=d, falsed=f, Ups=Ups, pole=pole or p, name=name) 

else: 

u = toUtm8(lat, lon, datum=d, falsed=f, Utm=Utm, name=name) 

return u 

 

 

def UtmUps(zone, hemipole, easting, northing, band='', datum=Datums.WGS84, 

falsed=True, name=''): 

'''Class-like function to create a UTM/UPS coordinate. 

 

@keyword zone: The UTM (longitudinal) zone with/-out Band 

letter for UTM or for UPS zone C{"00"} or 

C{0} (C{str} or C{int}). 

@keyword hemipole: UTM hemisphere or UPS top/center of projection 

(C{str}, C{'N[orth]'} or C{'S[outh]'}). 

@param easting: Easting, see B{C{falsed}} (C{meter}). 

@param northing: Northing, see B{C{falsed}} (C{meter}). 

@keyword band: Optional, UTM (latitudinal) Band letter 

C{'C'|'D'..'W'|'X'} or UPS (polar) Band letter 

C{'A'|'B'|'Y'|'Z'} (C{str}). 

@keyword datum: Optional, the coordinate's datum (L{Datum}). 

@keyword falsed: Both B{C{easting}} and B{C{northing}} are falsed (C{bool}). 

@keyword name: Optional name (C{str}). 

 

@return: New UTM or UPS instance (L{Utm} or L{Ups}). 

 

@raise UTMUPSError: UTM or UPS validation failed. 

 

@see: Classes L{Utm} and L{Ups} and Karney's U{UTMUPS 

<https://GeographicLib.SourceForge.io/html/classGeographicLib_1_1UTMUPS.html>}. 

''' 

z, B, hp = _to3zBhp(zone, band=band, hemipole=hemipole) 

U = Ups if z in (_UPS_ZONE, _UPS_ZONE_STR) else Utm 

return U(z, hp, easting, northing, band=B, datum=datum, falsed=falsed, name=name) 

 

 

def utmupsValidate(coord, falsed=False, MGRS=False): 

'''Check a UTM or UPS coordinate. 

 

@param coord: The UTM or UPS coordinate (L{Utm}, L{Ups} or C{5+Tuple}). 

@keyword falsed: C{5+Tuple} easting and northing are falsed (C{bool}). 

@keyword MGRS: Increase easting and northing ranges (C{bool}). 

 

@return: C{None} if validation passed. 

 

@raise UTMUPSError: Validation failed. 

 

@see: Function L{utmupsValidateOK}. 

''' 

 

def _en(en, lo, hi, ename): 

try: 

if lo <= float(en) <= hi: 

return 

except (TypeError, ValueError): 

pass 

t = '%s range [%.0f, %.0f]' % (U, lo, hi) 

raise UTMUPSError('%s outside %s: %g' % (ename, t, en)) 

 

if isinstance(coord, (Ups, Utm)): 

zone = coord.zone 

hemi = coord.hemisphere 

e, n = coord.easting, coord.northing 

band = coord.band 

enMM = coord.falsed 

elif isinstance(coord, (UtmUps5Tuple, UtmUps8Tuple)): 

zone = coord.zone 

hemi = coord.hemipole 

e, n = coord.easting, coord.northing 

band = coord.band 

enMM = falsed 

else: 

raise UTMUPSError('%s invalid: %r' % ('coord', coord)) 

 

z, B, h = _to3zBhp(zone, band, hemipole=hemi) 

 

if z == _UPS_ZONE: # UPS 

import pygeodesy.ups as u # PYCHOK expected 

U, M = 'UPS', _UpsMinMax 

else: # UTM 

import pygeodesy.utm as u # PYCHOK expected 

U, M = 'UTM', _UtmMinMax 

 

if MGRS: 

U, s = 'MGRS', _MGRS_TILE 

else: 

s = 0 

 

U = '%s %s%s %s' % (U, z,B, h) 

 

i = 'SN'.find(h) 

if i < 0 or z < _UTMUPS_ZONE_MIN \ 

or z > _UTMUPS_ZONE_MAX \ 

or B not in u._Bands: 

raise UTMUPSError('%s %s, %s or %s invalid: %r' % (U, 

'zone', 'hemisphere', 'band', (zone, hemi, band))) 

 

if enMM: 

_en(e, M.eMin[i] - s, M.eMax[i] + s, 'easting') # PYCHOK .eMax .eMin 

_en(n, M.nMin[i] - s, M.nMax[i] + s, 'northing') # PYCHOK .nMax .nMin 

 

 

def utmupsValidateOK(coord, falsed=False, ok=OK): 

'''Check a UTM or UPS coordinate. 

 

@param coord: The UTM or UPS coordinate (L{Utm}, L{Ups} or C{5+Tuple}). 

@keyword falsed: C{5+Tuple} easting and northing are falsed (C{bool}). 

@keyword ok: Result to return if validation passed (B{C{ok}}). 

 

@return: B{C{ok}} if validation passed, the L{UTMUPSError} otherwise. 

 

@see: Function L{utmupsValidate}. 

''' 

try: 

utmupsValidate(coord, falsed=falsed) 

return ok 

except UTMUPSError as x: 

return x 

 

 

def utmupsZoneBand5(lat, lon, cmoff=False): 

'''Return the UTM/UPS zone number, Band letter, hemisphere/pole 

and clipped lat- and longitude for a given location. 

 

@param lat: Latitude in degrees (C{scalar} or C{str}). 

@param lon: Longitude in degrees (C{scalar} or C{str}). 

@keyword cmoff: Offset longitude from the zone's central 

meridian, for UTM only (C{bool}). 

 

@return: A L{UtmUpsLatLon5Tuple}C{(zone, band, hemipole, 

lat, lon)} where C{hemipole} is C{'N'|'S'}, the 

UTM hemisphere or UPS pole, the UPS projection 

top/center. 

 

@raise RangeError: If B{C{lat}} outside the valid UTM or UPS 

bands or if B{C{lat}} or B{C{lon}} outside 

the valid range and L{rangerrors} set to 

C{True}. 

 

@raise ValueError: Invalid B{C{lat}} or B{C{lon}}. 

 

@see: Functions L{utmZoneBand5} and L{upsZoneBand5}. 

''' 

try: 

return utmZoneBand5(lat, lon, cmoff=cmoff) 

except RangeError: 

return upsZoneBand5(lat, lon) 

 

# **) MIT License 

# 

# Copyright (C) 2016-2020 -- 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.