forked from NationalSecurityAgency/qgis-latlontools-plugin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutil.py
169 lines (161 loc) · 6.86 KB
/
util.py
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
import math
import re
from qgis.core import QgsCoordinateReferenceSystem
epsg4326 = QgsCoordinateReferenceSystem('EPSG:4326')
def formatDmsString(lat, lon, isdms=False, prec=0, order=0, delimiter=', '):
'''Return a DMS formated string.'''
if order == 0: # Y, X or Lat, Lon
return convertDD2DMS(lat, True, isdms, prec) + str(delimiter) + convertDD2DMS(lon, False, isdms, prec)
else:
return convertDD2DMS(lon, False, isdms, prec) + str(delimiter) + convertDD2DMS(lat, True, isdms, prec)
def convertDD2DMS(coord, islat, isdms, prec):
'''Convert decimal degrees to DMS'''
if islat:
if coord < 0:
unit = 'S'
else:
unit = 'N'
else:
if coord > 0:
unit = 'E'
else:
unit = 'W'
coord = math.fabs(coord)
deg = math.floor(coord)
dmin = (coord - deg) * 60.0
min = math.floor(dmin)
sec = (dmin - min) * 60.0
if prec == 0:
dprec = 2
else:
dprec = prec + 3
if isdms:
s = "{:.0f}\xB0{:.0f}\'{:.{prec}f}\"".format(deg, min, sec, prec=prec)
else:
if islat:
s = "{:02.0f}{:02.0f}{:0{dprec}.{prec}f}".format(deg, min, sec, dprec=dprec, prec=prec)
else:
s = "{:03.0f}{:02.0f}{:0{dprec}.{prec}f}".format(deg, min, sec, dprec=dprec, prec=prec)
if isdms:
s += " " + unit
else:
s += unit
return(s)
def parseDMSString(str, order=0):
'''Parses a pair of coordinates that are in the order of
"latitude, longitude". The string can be in DMS or decimal
degree notation. If order is 0 then then decimal coordinates are assumed to
be in Lat Lon order otherwise they are in Lon Lat order. For DMS coordinates
it does not matter the order.'''
str = str.strip().upper() # Make it all upper case
try:
if re.search(r"[NSEW]", str) is None:
# There were no annotated dms coordinates so assume decimal degrees
# Remove any characters that are not digits and decimal
str = re.sub(r"[^\d.+-]+", " ", str).strip()
coords = re.split(r'\s+', str, 1)
if len(coords) != 2:
raise ValueError('Invalid Coordinates')
if order == 0:
lat = float(coords[0])
lon = float(coords[1])
else:
lon = float(coords[0])
lat = float(coords[1])
else:
# We should have a DMS coordinate
if re.search(r'[NSEW]\s*\d+.+[NSEW]\s*\d+', str) is None:
# We assume that the cardinal directions occur after the digits
m = re.findall(r'(.+)\s*([NS])[\s,;:]*(.+)\s*([EW])', str)
if len(m) != 1 or len(m[0]) != 4:
# This is either invalid or the coordinates are ordered by lon lat
m = re.findall(r'(.+)\s*([EW])[\s,;:]*(.+)\s*([NS])', str)
if len(m) != 1 or len(m[0]) != 4:
# Now we know it is invalid
raise ValueError('Invalid DMS Coordinate')
else:
# The coordinates were in lon, lat order
lon = parseDMS(m[0][0], m[0][1])
lat = parseDMS(m[0][2], m[0][3])
else:
# The coordinates are in lat, lon order
lat = parseDMS(m[0][0], m[0][1])
lon = parseDMS(m[0][2], m[0][3])
else:
# The cardinal directions occur at the beginning of the digits
m = re.findall(r'([NS])\s*(\d+.*?)[\s,;:]*([EW])(.+)', str)
if len(m) != 1 or len(m[0]) != 4:
# This is either invalid or the coordinates are ordered by lon lat
m = re.findall(r'([EW])\s*(\d+.*?)[\s,;:]*([NS])(.+)', str)
if len(m) != 1 or len(m[0]) != 4:
# Now we know it is invalid
raise ValueError('Invalid DMS Coordinate')
else:
# The coordinates were in lon, lat order
lon = parseDMS(m[0][1], m[0][0])
lat = parseDMS(m[0][3], m[0][2])
else:
# The coordinates are in lat, lon order
lat = parseDMS(m[0][1], m[0][0])
lon = parseDMS(m[0][3], m[0][2])
except Exception:
raise ValueError('Invalid Coordinates')
return lat, lon
def parseDMSStringSingle(str):
'''Parse a single coordinate either DMS or decimal degrees.
It simply returns the value but doesn't maintain any knowledge
as to whether it is latitude or longitude'''
str = str.strip().upper()
try:
if re.search(r"[NSEW]", str) is None:
coord = float(str)
else:
# We should have a DMS coordinate
if re.search(r'[NSEW]\s*\d+', str) is None:
# We assume that the cardinal directions occur after the digits
m = re.findall(r'(.+)\s*([NSEW])', str)
if len(m) != 1 or len(m[0]) != 2:
raise ValueError('Invalid DMS Coordinate')
coord = parseDMS(m[0][0], m[0][1])
else:
# The cardinal directions occur at the beginning of the digits
m = re.findall(r'([NSEW])\s*(.+)', str)
if len(m) != 1 or len(m[0]) != 2:
raise ValueError('Invalid DMS Coordinate')
coord = parseDMS(m[0][1], m[0][0])
except Exception:
raise ValueError('Invalid Coordinates')
return coord
def parseDMS(str, hemisphere):
'''Parse a DMS formatted string.'''
str = re.sub(r"[^\d.]+", " ", str).strip()
parts = re.split(r'[\s]+', str)
dmslen = len(parts)
if dmslen == 3:
deg = float(parts[0]) + float(parts[1]) / 60.0 + float(parts[2]) / 3600.0
elif dmslen == 2:
deg = float(parts[0]) + float(parts[1]) / 60.0
elif dmslen == 1:
dms = parts[0]
if hemisphere == 'N' or hemisphere == 'S':
dms = '0' + dms
# Find the length up to the first decimal
ll = dms.find('.')
if ll == -1:
# No decimal point found so just return the length of the string
ll = len(dms)
if ll >= 7:
deg = float(dms[0:3]) + float(dms[3:5]) / 60.0 + float(dms[5:]) / 3600.0
elif ll == 6: # A leading 0 was left off but we can still work with 6 digits
deg = float(dms[0:2]) + float(dms[2:4]) / 60.0 + float(dms[4:]) / 3600.0
elif ll == 5:
deg = float(dms[0:3]) + float(dms[3:]) / 60.0
elif ll == 4: # Leading 0's were left off
deg = float(dms[0:2]) + float(dms[2:]) / 60.0
else:
deg = float(dms)
else:
raise ValueError('Invalid DMS Coordinate')
if hemisphere == 'S' or hemisphere == 'W':
deg = -deg
return deg