forked from heldersepu/GMapCatcher
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmapUtils.py
348 lines (283 loc) · 11.3 KB
/
mapUtils.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
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
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# -*- coding: utf-8 -*-
## @package gmapcatcher.mapUtils
# A group of map utilities
import re
import os
import math
from mapConst import *
from widgets.customWidgets import FileChooser, FileSaveChooser
from time import gmtime, strftime
from htmlentitydefs import name2codepoint
try:
from gmapcatcher.gpxpy import gpx, parse
except:
pass
def tiles_on_level(zoom_level):
return 1 << (MAP_MAX_ZOOM_LEVEL - int(zoom_level))
def tile_adjustEx(zoom_level, tile, offset):
world_tiles = tiles_on_level(zoom_level)
x = int((tile[0] * TILES_WIDTH + offset[0]) % (world_tiles * TILES_WIDTH))
y = int((tile[1] * TILES_HEIGHT + offset[1]) % (world_tiles * TILES_HEIGHT))
tile_coord = (x / int(TILES_WIDTH), y / int(TILES_HEIGHT))
offset_in_tile = (x % int(TILES_WIDTH), y % int(TILES_HEIGHT))
return tile_coord, offset_in_tile
def tile_adjust(zoom_level, tile):
world_tiles = tiles_on_level(zoom_level)
return (int(tile[0]) % world_tiles, int(tile[1]) % world_tiles)
def mod(x, y):
r = x % y
if r < 0:
r += y
return r
## Convert from coord(lat, lng, zoom_level) to (tile, offset)
def coord_to_tile(coord):
world_tiles = tiles_on_level(coord[2])
x = world_tiles / 360.0 * (coord[1] + 180.0)
tiles_pre_radian = world_tiles / (2 * math.pi)
e = math.sin(coord[0] * (1 / 180. * math.pi))
y = world_tiles / 2 + 0.5 * math.log((1 + e) / (1 - e)) * (-tiles_pre_radian)
offset = int((x - int(x)) * TILES_WIDTH), \
int((y - int(y)) * TILES_HEIGHT)
return (int(x) % world_tiles, int(y) % world_tiles), offset
## Convert from ((tile, offset), zoom_level) to coord(lat, lon, zoom_level)
def tile_to_coord(tile, zoom):
world_tiles = tiles_on_level(zoom)
x = (tile[0][0] + 1.0 * tile[1][0] / TILES_WIDTH) / (world_tiles / 2.) - 1 # -1...1
y = (tile[0][1] + 1.0 * tile[1][1] / TILES_HEIGHT) / (world_tiles / 2.) - 1 # -1...1
lon = x * 180.0
y = math.exp(-y * 2 * math.pi)
e = (y - 1) / (y + 1)
lat = 180.0 / math.pi * math.asin(e)
return lat, lon, zoom
## Convert a list (iterable) of coords (lat,lon) into a set of tiles
def coords_to_tilepath(coords, zoom):
res = set()
ltile = None
for lat, lon in coords:
tile = coord_to_tile((lat, lon, zoom))
if ltile is None:
res.add(tile[0])
elif ltile[0] == tile[0]:
pass
else:
a = ltile[0]
b = tile[0]
### Adds the tile path in between
if a[0] == b[0]:
for p in range(min(a[1], b[1]), max(a[1], b[1]) + 1):
res.add((a[0], p))
elif a[1] == b[1]:
for p in range(min(a[0], b[0]), max(a[0], b[0]) + 1):
res.add((p, a[1]))
else:
d0 = b[0] - a[0]
d1 = b[1] - a[1]
if abs(d0) > abs(d1): # Horizontal scan
for p in range(d0 + 1):
res.add((a[0] + p, int(a[1] + float(d1 * p) / float(d0) - 0.5)))
res.add((a[0] + p, int(a[1] + float(d1 * p) / float(d0) + 0.5)))
else:
for p in range(d1 + 1):
res.add((int(a[0] + float(d0 * p) / float(d1) - 0.5), a[1] + p))
res.add((int(a[0] + float(d0 * p) / float(d1) + 0.5), a[1] + p))
res.add(tile[0])
#print tile
ltile = tile
return res
def tilepath_bulk(tiles, size):
res = set()
for x, y in tiles:
for dx in range(-size, size + 1):
for dy in range(-size, size + 1):
res.add((x + dx, y + dy))
return res
## Find scale of the picture in km per pixel
def km_per_pixel(coord):
osm_zoom = MAP_MAX_ZOOM_LEVEL - coord[2]
S = ((2 * math.pi * R_EARTH) * math.cos(math.radians(coord[0]))) / (2 ** (osm_zoom + 8))
return S
## Returns tuple with scale length in pixels and configured units.
def friendly_scale(zoomlevel, latitude=0, units=UNIT_TYPE_KM):
distance = sig_figs(km_per_pixel((latitude, 0, zoomlevel)), 4)
if units != UNIT_TYPE_KM:
distance = convertUnits(UNIT_TYPE_KM, units, distance)
return (150, distance * 150)
## Convert tuple-like string to real tuples
# eg: '((1, 2), (2, 3))' -> ((1, 2), (2, 3))
def str_to_tuple(strCenter):
strCenter = strCenter.strip()
strCenter = strCenter.replace('(', '')
strCenter = strCenter.replace(')', '')
center = map(int, strCenter.split(','))
return ((center[0], center[1]),
(center[2], center[3]))
def nice_round(f):
return sig_figs(f, 2)
def sig_figs(f, sf):
n = int(math.log(f, 10))
return round(f, sf - n)
## Convert from ((tile, zoom), rect, center) to screen coordinates
def tile_coord_to_screen(tile_coord, rect, center, getGlobal=False):
dx = rect.width // 2 - center[1][0] + (tile_coord[0] - center[0][0]) * TILES_WIDTH
dy = rect.height // 2 - center[1][1] + (tile_coord[1] - center[0][1]) * TILES_HEIGHT
if getGlobal or (-TILES_WIDTH <= dx <= rect.width and -TILES_HEIGHT <= dy <= rect.height):
return [(int(dx), int(dy))]
else:
return None
## Convert from screen pointer to tile
def pointer_to_tile(rect, pointer, center, zl):
da_center = (rect.width // 2, rect.height // 2)
fix_tile = center[0]
fix_offset = center[1][0] + (pointer[0] - da_center[0]), \
center[1][1] + (pointer[1] - da_center[1])
return tile_adjustEx(zl, fix_tile, fix_offset)
def pointer_to_coord(rect, pointer, center, zl):
tile = pointer_to_tile(rect, pointer, center, zl)
return tile_to_coord(tile, zl)
## Convert km to latitude
def km_to_lat(height):
return height * 180 / math.pi / R_EARTH
## Convert km to longitude
def km_to_lon(width, lat):
return width * 180 / math.pi / (R_EARTH * math.cos(lat * math.pi / 180))
## Return Date & Time
def timeStamp():
return strftime(" %d_%b_%Y %H.%M.%S", gmtime())
## Convert altitude (meters) to zoom
def altitude_to_zoom(altitude):
if int(altitude) <= 0:
zoom = MAP_MIN_ZOOM_LEVEL + 2
else:
zoom = int(math.log(int(altitude)) / math.log(2))
return min(max(zoom, MAP_MIN_ZOOM_LEVEL + 2), MAP_MAX_ZOOM_LEVEL)
def subs_entity(match):
entity = match.group(3)
if match.group(1) == "#":
if match.group(2) == '':
return unichr(int(entity))
elif match.group(2) == 'x':
return unichr(int('0x' + entity, 16))
else:
codepoint = name2codepoint.get(entity, "")
if codepoint != "":
return unichr(codepoint)
return match.group()
def html_decode(string):
entity_re = re.compile("&(#?)(x?)(\d{1,5}|\w{1,8});")
return entity_re.subn(subs_entity, string)[0]
def countDistanceFromLatLon(a, b):
dLat = math.radians(a[0] - b[0])
dLon = math.radians(a[1] - b[1])
lat1 = math.radians(a[0])
lat2 = math.radians(b[0])
a = math.sin(dLat / 2) * math.sin(dLat / 2) + math.sin(dLon / 2) * math.sin(dLon / 2) * math.cos(lat1) * math.cos(lat2)
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
d = float(R_EARTH * c)
return d
def countBearingFromLatLon(a, b):
y = math.sin(a[1] - b[1]) * math.cos(b[0])
x = math.cos(a[0]) * math.sin(b[0]) - \
math.sin(a[0]) * math.cos(b[0]) * math.cos(a[1] - b[1])
bearing = (((math.degrees(math.atan2(y, x)) + 360) % 360) + 180) % 360
return bearing
def saveGPX(trackSegments):
f_name = FileSaveChooser(USER_PATH, strTitle="Select File")
if f_name:
gpx_export = gpx.GPX()
gpx_track = gpx.GPXTrack()
gpx_track.name = NAME + ' Tracks'
gpx_export.tracks.append(gpx_track)
for trackSegment in trackSegments:
gpx_segment = gpx.GPXTrackSegment()
gpx_track.segments.append(gpx_segment)
for p in trackSegment.points:
point = gpx.GPXTrackPoint(p.latitude, p.longitude)
if p.altitude:
point.elevation = p.altitude
if p.timestamp:
point.time = p.timestamp
if p.speed:
point.speed = p.speed
gpx_segment.points.append(point)
f = open(f_name, 'w')
f.write(gpx_export.to_xml())
f.close()
def openGPX(f_name = None):
tracks = None
if (f_name is None):
f_name = FileChooser(USER_PATH, strTitle="Select File")
if f_name:
f = open(f_name, 'r')
tracks = list()
gpx_import = parse(f)
f.close()
f_name = os.path.basename(f_name)
i = 1
for track in gpx_import.tracks:
for segment in track.segments:
track_points = list()
for point in segment.points:
track_points.append(TrackPoint(point.latitude, point.longitude, point.time, point.elevation, point.speed))
if len(track.segments) > 1 or len(gpx_import.tracks) > 1:
tracks.append(Track(track_points, '%s - track %i' % (f_name, i)))
if len(track.segments) > 1:
i += 1
else:
tracks.append(Track(track_points, '%s - track' % f_name))
i += 1
i = 0
for route in gpx_import.routes:
track_points = list()
for point in route.points:
track_points.append(TrackPoint(point.latitude, point.longitude, point.time, point.elevation, point.speed))
if len(gpx_import.routes) > 1:
tracks.append(Track(track_points, '%s - route %i' % (f_name, i)))
else:
tracks.append(Track(track_points, '%s - route' % f_name))
i += 1
waypoints = list()
for waypoint in gpx_import.waypoints:
waypoints.append(TrackPoint(waypoint.latitude, waypoint.longitude))
if len(waypoints) >= 1:
tracks.append(Track(waypoints, '%s - waypoints' % f_name))
return tracks
def convertUnits(unit_from, unit_to, value):
if unit_from == UNIT_TYPE_KM:
if unit_to == UNIT_TYPE_MILE:
return float(value) / 1.609344
elif unit_to == UNIT_TYPE_NM:
return float(value) / 1.852
elif unit_from == UNIT_TYPE_MILE:
if unit_to == UNIT_TYPE_KM:
return float(value) * 1.609344
elif unit_to == UNIT_TYPE_NM:
return float(value) * 0.868976242
elif unit_from == UNIT_TYPE_NM:
if unit_to == UNIT_TYPE_KM:
return float(value) * 1.852
elif unit_to == UNIT_TYPE_MILE:
return float(value) * 1.15077945
return value
class Track:
def __init__(self, points, name=None, distance=None):
self.points = points
self.name = name
if distance:
self.distance = distance
else:
self.recalculateDistance()
def recalculateDistance(self):
distance = 0
for i in range(0, len(self.points) - 1):
distance += countDistanceFromLatLon(self.points[i].getLatLon(), self.points[i + 1].getLatLon())
self.distance = distance
return self.distance
class TrackPoint:
def __init__(self, latitude=None, longitude=None, timestamp=None, altitude=None, speed=None):
self.latitude = latitude
self.longitude = longitude
self.timestamp = timestamp
self.altitude = altitude
self.speed = speed
def getLatLon(self):
return (self.latitude, self.longitude)