:: INTRO
Google API를 쓰다보면 가끔 위도, 경도 (Latitude, Longitude)가 아닌 Polyline 이라는 알 수 없는 포멧으로 데이터를 주는 경우가 있다. 실제로 해당 데이터를 받아보면 이게 무엇을 의미하는지 정말 전혀 알아 볼 수 없다.
당장 구현을 해야하는데 이게 대체 무엇을 의미하는 건지 알 수 가 없어서 생존을 위해 열심히 찾아본 것들을 본 포스팅에서 간단히 정리 하려고 한다.
:: Polyline이 뭔데 ?
Polyline은 일련의 좌표들을 '단일 문자열'로 저장 할 수 있게끔 해주는 손실 압축 알고리즘이다. 한 개 지점에 대한 위도, 경도가 아닌 여러 지점에 대한 위도 경도를 다뤄야 할 때는 [ (a,b), (c,d), (e,f) ] 등으로 자료를 다루어야 하는데, 이때 a,b,c,d,e,f 는 단순 정수가 아닌 소수점 형태로 이루어져 있다는 것을 감안하면 주고 받기 힘든 자료형태인데 이를 하나의 문자열로 주고 받을 수 있게끔 해주는 알고리즘 이라고 생각하면 된다.
손실 압축 알고리즘인 이유는 초기값에서 변환할 때 일정 부분을 반올림 하게 되는 과정을 거치기에 초기 가리키던 정확한 위치 정보와 미세한 차이를 보이기 때문이다. 그럼 Polyline이 어떻게 변환 되는지를 한번 보자.
1) 초기 값 가져오기
-179.9832104 을 초기값으로 하자.
2) 소수값에서 1e5 = 10,000을 곱하고 소수 첫째 자리에서 반올림하기
-179.9832104 * 10,000 = -17998321.04
소수 첫째 자리에서 반올림하여 정수화 = -17998321
3) 10진수 값을 2진수로 변환하기, 이때 음수라면 2의 보수를 취할 것.
일단 17998321을 2진수로 변환하면
0000 0001 0001 0010 1010 0001 1111 0001
여기서 기존의 값은 음수였기 때문에 반전 후
1111 1110 1110 1101 0101 1110 0000 1110
2의 보수를 취하기위해 1를 더해주면
1111 1110 1110 1101 0101 1110 0000 1111 이 된다.
4) 해당 2진 값을 1비트 왼쪽으로 SHIFT 연산한다.
1111 1101 1101 1010 1011 1100 0001 1110
5) 원래 초기값이 음수라면 해당 값을 반전 한다.
0000 0010 0010 0101 0100 0011 1110 0001
6) 해당 값을 오른쪽을 기준으로 5비트씩 나눈다.
00001 00010 01010 10000 11111 00001
7) 해당 값을 역순으로 배치 한다.
00001 11111 10000 01010 00010 00001
8) 해당 값만이 아닌 다른 비트 청크가 따르는 경우라면 각각의 값을 0X20 = 100000(2) 값으로 OR 연산
00001 | 100000 = 100001
11111 | 100000 = 111111
.... 전부 다 변환 하면
100001 111111 110000 101010 100010 000001
9) 각 자리 값을 10진수로 변환
33 63 48 42 34 1
10) 각 변환 값에 63을 더함
96 126 111 105 97 64
11) 해당 값을 ASCII 코드 값으로 변환
.`~ oia @
쯔안 ~ 이렇게하면 우리의 -179.9832104 값을 Polyline으로 변환하여 .`~ oia @ 이라는 값을 얻을 수 있다. 이걸 이제 모든 좌표에 대해 해주면 되는데, 사실 중요한건 원리도 원리인데 그래서 당장 구현을 어떻게 해야하는가 ? 이기에 바로 패키지를 사용해서 간단히 다뤄보도록 하자.
:: 패키지를 이용해 Polyline 다뤄보기
다행히 우리의 파이썬에는 라이브러리가 존재한다. 감사합니다 오픈소스 .. 언젠가 저도 꼭 기여 할께요.
간단히 pip를 이용해 라이브러리를 설치해주자.
pip install polyline
설치가 되었다면 먼저 위도,경도를 PolyLine으로 변경해보자.
import polyline as poly
# LatLon to Polyline
latlon = [(38.5, -120.2), (40.7, -120.9), (43.2, -126.4)]
toPoly = poly.encode(latlon, precision=5)
print(toPoly)
이때 precision은 손실 압축 과정에서 어디까지 가지고 갈것인지에 대한 값으로 위 변환 예시중 2)번에 대한 파라미터 조정 값이다. 기본적으로 5로 설정 되어있다.
import polyline as poly
# LatLon to Polyline
latlon = [(38.5, -120.2), (40.7, -120.9), (43.2, -126.4)]
toPoly = poly.encode(latlon, precision=5)
print(toPoly)
# Polyline to LatLon
polyData = "_p~iF~ps|U_ulL~ugC_hgN~eq`@"
toLatLon = poly.decode(polyData)
print(toLatLon)
두개의 결과값을 찍어보면 아래와 같이 같은 값으로 잘 변환이 되고 있음을 확인 할 수 있다 !