JuniorPolegato

Google Maps Polyline

Nov 19th, 2012
94
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.17 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3.  
  4. # Author: Claudio Polegato Junior
  5. # Date..: 18/Nov/2012
  6. # E-mail: linux.at.juniorpolegato.com.br
  7. # File..: gmaps_polyline.py
  8. # Descr.: Encode and decode latitude, longitude, level and polyline
  9.  
  10. def gm_encode(number, encode_level = False):
  11.     ('Encode number for polyline in Google Maps. To encode level, just'
  12.      'skip the 6 first steps. From: https://developers.google.com/maps'
  13.      '/documentation/utilities/polylinealgorithm')
  14.     if not encode_level:
  15.         # Take the initial signed value: number
  16.         #-179.9832104
  17.         # Take the decimal value and multiply it by 1e5,
  18.         #rounding the result:
  19.         number = int(round(number * 1e5))
  20.         #Convert the decimal value to binary. Note that a negative
  21.         #value must be calculated using its two's complement
  22.         #by inverting the binary value and adding one to the result:
  23.         #00000001 00010010 10100001 11110001
  24.         #11111110 11101101 01011110 00001110
  25.         #11111110 11101101 01011110 00001111
  26.         if number < 0:
  27.             number = -number - (1 << 32)
  28.         #Left-shift the binary value one bit:
  29.         #11111101 11011010 10111100 00011110
  30.         number = number << 1
  31.     #If the original decimal value is negative, invert this encoding:
  32.     #00000010 00100101 01000011 11100001
  33.     if number < 0:
  34.         # exclude -0b and change 0 and 1
  35.         binary = bin(number)[3:].replace('0', '\0').replace('1', '0').\
  36.                                                       replace('\0', '1')
  37.         # get 32 lowest bits completing with 1 at right
  38.         # can be 30 bits, because we will use just 30, but...
  39.         binary = ('1' * 31 + binary)[-32:]
  40.     else:
  41.         # exclude 0b
  42.         binary = bin(number)[2:]
  43.         # get 32 lowest bits completing with 0 at right
  44.         # can be 30 bits, because we will use just 30, but...
  45.         binary = ('0' * 31 + binary)[-32:]
  46.     #Break the binary value out into 5-bit chunks
  47.     #(starting from the right hand side):
  48.     #00001 00010 01010 10000 11111 00001
  49.     #Place the 5-bit chunks into reverse order:
  50.     #00001 11111 10000 01010 00010 00001
  51.     chunks = [binary[i - 5:i] for i in range(32, 2, -5)]
  52.     #-----------> The step to exclude '00000' from the end was forgotten
  53.     while chunks[-1] == '00000':
  54.         chunks.pop()
  55.     #OR each value with 0x20 if another bit chunk follows:
  56.     #100001 111111 110000 101010 100010 000001
  57.     chunks = ['1' + c for c in chunks[:-1]] + [chunks[-1]]
  58.     #Convert each value to decimal:
  59.     #33 63 48 42 34 1
  60.     #Add 63 to each value:
  61.     #96 126 111 105 97 64
  62.     #Convert each value to its ASCII equivalent:
  63.     #`~oia@
  64.     return ''.join([chr(int(c, 2) + 63) for c in chunks])
  65.  
  66. def gm_decode(encoded_number, decode_level = False):
  67.     # reverse return above
  68.     chunks = [bin(ord(c) - 63)[2:] for c in encoded_number]
  69.     # remove the marks '1' from chunks, except the last
  70.     chunks = [chunk[1:] for chunk in chunks[:-1]] + [chunks[-1]]
  71.     # restore binary bits join chunks of a value in reverse order
  72.     binary = ''.join(chunks[::-1])
  73.     if not decode_level:
  74.         # if the lowest bit is 1, then the number is negative, so we
  75.         # need to do the complement, ignoring the lowest bit
  76.         if binary[-1] == '1':
  77.             number = -(int(binary, 2) >> 1) - 1
  78.         else:
  79.             # get the number casting binary ignoring the lowest bit
  80.             number = int(binary, 2) >> 1
  81.         # divide the number by 1e5
  82.         number /= 1e5
  83.     else:
  84.         # when in decode_level, just cast binary to int
  85.         number = int(binary, 2)
  86.     return number
  87.  
  88. def gm_encode_polyline(polyline, print_table = False):
  89.     if print_table:
  90.         print ('\n|  Latitude| Longitude|Change In Latitude|Change In'
  91.                ' Longitude|Encoded Latitude|Encoded Longitude|Encoded'
  92.                ' Point|')
  93.     last = (0, 0)
  94.     encoded_polyline = ''
  95.     for latitude, longitude in polyline:
  96.         # calcule the distance from the last point
  97.         point = (latitude - last[0], longitude - last[1])
  98.         # last point is this point now
  99.         last = (latitude, longitude)
  100.         # encode the distance
  101.         encoded = (gm_encode(point[0]), gm_encode(point[1]))
  102.         if print_table:
  103.             print '|%10.5f|%10.5f|%18.5f|%19.5f|%16s|%17s|%13s|' % (
  104.                     latitude, longitude, point[0], point[1],
  105.                     encoded[0], encoded[1], ''.join(encoded))
  106.         # join the encoded values with encoded_polyline
  107.         encoded_polyline += ''.join(encoded)
  108.     return encoded_polyline
  109.  
  110. def gm_decode_polyline(encoded_polyline):
  111.     polyline = []
  112.     latitude = None
  113.     # split the encoded_polyline where the ord - 63 of last caracter is
  114.     # less than 0x20, this is the marks in gm_encode for the last one
  115.     splits = [c for c in encoded_polyline if ord(c) - 63 < 0x20]
  116.     for c in splits:
  117.         # get the encoded value and rest of encoded_polyline
  118.         value, encoded_polyline = encoded_polyline.split(c, 1)
  119.         # decode the value
  120.         value = gm_decode(value + c)
  121.         # if latitude is none, then this value is the latitude
  122.         if latitude is None:
  123.             latitude = value
  124.         # else, if we had have latitude, this value is the longitude
  125.         # now we have a point
  126.         else:
  127.             # if we have a point in polyline, this point is the
  128.             # distance, then we plus this point with the last
  129.             if polyline:
  130.                 last = polyline[-1]
  131.                 polyline.append((last[0] + latitude, last[1] + value))
  132.             # else, this is the first point
  133.             else:
  134.                 polyline.append((latitude, value))
  135.             # latitude is none to get a new latitude
  136.             latitude = None
  137.     # return polyline like a tuple
  138.     return tuple(polyline)
  139.  
  140. print '\nTeste if encode of -179.9832104 is "`~oia@"...'
  141. print "Great! Excelente!" if gm_encode(-179.9832104) == "`~oia@"\
  142.                                            else "Oh no, theres a error!"
  143.  
  144. print '\nTeste if decode of "`~oia@" is -179.98321...'
  145. print "Great! Excelente!" if gm_decode("`~oia@") == -179.98321\
  146.                                            else "Oh no, theres a error!"
  147.  
  148. print '\nTeste if encode of level 174 is "mD"...'
  149. print "Great! Excelente!" if gm_encode(174, True) == "mD"\
  150.                                            else "Oh no, theres a error!"
  151.  
  152. print '\nTeste if decode of level "mD" is 174...'
  153. print "Great! Excelente!" if gm_decode("mD", True) == 174\
  154.                                            else "Oh no, theres a error!"
  155.  
  156. polyline = ((38.5, -120.2), (40.7, -120.95), (43.252, -126.453))
  157. encoded_polyline = gm_encode_polyline(polyline, True)
  158.  
  159. print "\nEncoded Polyline:", encoded_polyline
  160. print "Great! Excelente!" if encoded_polyline ==\
  161.              "_p~iF~ps|U_ulLnnqC_mqNvxq`@" else "Oh no, theres a error!"
  162.  
  163. print ('\nTeste if decode of decode of polyline '
  164.            '"_p~iF~ps|U_ulLnnqC_mqNvxq`@" is ' + repr(polyline) + '...')
  165. print "Great! Excelente!" if gm_decode_polyline(
  166.                              "_p~iF~ps|U_ulLnnqC_mqNvxq`@") == polyline\
  167.                                            else "Oh no, theres a error!"
Add Comment
Please, Sign In to add comment