-
Notifications
You must be signed in to change notification settings - Fork 1
/
polyline.py
118 lines (85 loc) · 3.33 KB
/
polyline.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
'''Provides utility functions for encoding and decoding linestrings using the
Google encoded polyline algorithm.
'''
def encode_coords(coords):
'''Encodes a polyline using Google's polyline algorithm
See http://code.google.com/apis/maps/documentation/polylinealgorithm.html
for more information.
:param coords: Coordinates to transform (list of tuples in order: latitude,
longitude).
:type coords: list
:returns: Google-encoded polyline string.
:rtype: string
'''
result = []
prev_lat = 0
prev_lng = 0
for x, y in coords:
lat, lng = int(y * 1e5), int(x * 1e5)
d_lat = _encode_value(lat - prev_lat)
d_lng = _encode_value(lng - prev_lng)
prev_lat, prev_lng = lat, lng
result.append(d_lat)
result.append(d_lng)
return ''.join(c for r in result for c in r)
def _split_into_chunks(value):
while value >= 32: #2^5, while there are at least 5 bits
# first & with 2^5-1, zeros out all the bits other than the first five
# then OR with 0x20 if another bit chunk follows
yield (value & 31) | 0x20
value >>= 5
yield value
def _encode_value(value):
# Step 2 & 4
value = ~(value << 1) if value < 0 else (value << 1)
# Step 5 - 8
chunks = _split_into_chunks(value)
# Step 9-10
return (chr(chunk + 63) for chunk in chunks)
def decode(point_str):
'''Decodes a polyline that has been encoded using Google's algorithm
http://code.google.com/apis/maps/documentation/polylinealgorithm.html
This is a generic method that returns a list of (latitude, longitude)
tuples.
:param point_str: Encoded polyline string.
:type point_str: string
:returns: List of 2-tuples where each tuple is (latitude, longitude)
:rtype: list
'''
# sone coordinate offset is represented by 4 to 5 binary chunks
coord_chunks = [[]]
for char in point_str:
# convert each character to decimal from ascii
value = ord(char) - 63
# values that have a chunk following have an extra 1 on the left
split_after = not (value & 0x20)
value &= 0x1F
coord_chunks[-1].append(value)
if split_after:
coord_chunks.append([])
del coord_chunks[-1]
coords = []
for coord_chunk in coord_chunks:
coord = 0
for i, chunk in enumerate(coord_chunk):
coord |= chunk << (i * 5)
#there is a 1 on the right if the coord is negative
if coord & 0x1:
coord = ~coord #invert
coord >>= 1
coord /= 100000.0
coords.append(coord)
# convert the 1 dimensional list to a 2 dimensional list and offsets to
# actual values
points = []
prev_x = 0
prev_y = 0
for i in xrange(0, len(coords) - 1, 2):
if coords[i] == 0 and coords[i + 1] == 0:
continue
prev_x += coords[i + 1]
prev_y += coords[i]
# a round to 6 digits ensures that the floats are the same as when
# they were encoded
points.append((round(prev_x, 6), round(prev_y, 6)))
return points