-
Notifications
You must be signed in to change notification settings - Fork 0
/
polyline.py
122 lines (88 loc) · 3.5 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
119
120
121
122
#!/usr/bin/python
# http://stackoverflow.com/questions/4639311/parsing-json-file-with-python-google-map-api
'''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)))
points.append((round(prev_y, 6), round(prev_x, 6)))
return points