Клуб API Карт

Пример кода на Python для кодирования вершин ломаной в Base64.

Пост в архиве.

Возникла задача отдавать карты используя static api яндекс.карт.

Как известно, координаты можно закодировать в base64, алгоритм приведен по ссылке: http://api.yandex.ru/maps/jsapi/doc/dg/tasks/how-to-add-polyline.xml#encoding-polyline-points

Так как сайт разрабатывается для мобильников, то на javascript не рассчитываю. Поэтому конвертирую координаты в base64 на сервере.

Сайт строится на Django, следовательно, написал метод на Python для конвертации. Метод привожу ниже, если есть какие-то замечания по улучшению, то милости просим.

 #Метод для кодирования вершин ломаной
#аргумент: строка с координатами разделенными запятой (пример: 37.593578,55.735094,37.592159,55.732469,37.589374,55.734162)
def encodeToBase64(aPoints):   
    keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_="
    coordTuple = eval(aPoints)
    coordList = []
    diffList = []
    reverseString = ""
    encodedString = ""
    for i in range(len(coordTuple)):
        coordList.append(int(coordTuple[i] * 1000000))
    diffList.append(toBin(coordList[0]))
    diffList.append(toBin(coordList[1]))   
    for index in range(len(coordList)):
        if index+2 < len(coordList):
            diffList.append(toBin(coordList[index+2] - coordList[index]))
    binString = "".join(diffList)
    for i in range(len(binString)/32):
        fourByteString = binString[i*32:(i+1)*32]
        byte1 = fourByteString[0:8]
        byte2 = fourByteString[8:16]
        byte3 = fourByteString[16:24]
        byte4 = fourByteString[24:32]
        reverseFourByteString = byte4 + byte3 + byte2 + byte1
        reverseString += reverseFourByteString
    for i in range(len(reverseString)/6):
        encodedString += keyStr[int(reverseString[i*6:(i+1)*6], 2)]
    remainder = len(binString)%6
    if remainder: 
        lastString = reverseString[-1*remainder:]
        for i in range(6 - remainder):
            lastString += "0"
        encodedString += keyStr[int(lastString, 2)]
        for j in range(0, 6-remainder, 2):
            encodedString += "="    
    return encodedString

#Метод для пробразования целого в 32-х битное двоичное предстваление
#аргументы: целое, count - количество битов
def toBin(x, count=32):
    return "".join(map(lambda y:str((x>>y)&1), range(count-1, -1, -1)))

4 комментария
Александр Новиков
28 января 2016, 06:26
А зачем надо было писать кодирование в base64? Это довольно распространенная операция и я уверен что на Питоне есть для этого уже готовые модули/функции.
Кодирование потребовалось для того, чтобы не кидать длинную цепочку координат вершин ломаной, которую я отображаю на статичных изображениях яндекс карт.
Да, есть даже библиотечная функция: binascii.b2a_base64(data), но алгоритм кодирования у яндекса отличается. Искал реализацию в клубе, не нашел, написал свою релизацию, может кому-нибудь пригодится или кто-то напишет лучше.
Я всегда считал что код на питоне очень легко читается.
Но конкретно этот - тяжело. Может дело в том что сжат и нет отступов,
а может это просто моя привычка не писать много вложенных вызовов типа: 

"".join(map(lambda y:str((x>>y)&1), range(count-1, -1, -1)))

тем более что списковые включения короче и лаконичнее:
"".join([str(x>>y&1) for y in range(count-1, -1, -1)])

Ну и если морочиться производительностью, то лучше не склеивать строки в цикле
а собирать из них массив и join-ить его.
Мой вариант конвертации:

https://github.com/Cjkjvfnby/Route/blob/master/route/base64.py