Encoding and Decoding Polylines with Erlang

If you have ever worked with the Google Directions API you probably came across encoded polylines. I wanted to decode and encode these using Erlang but was unable to find an existing implementation. So I decided to write my own.

Point = {Lng, Lat},
Path = [{Lng, Lat}, {Lng2, Lat2}].

The encode/1 function takes a path and returns an encoded polyline.

 1 encode(Path) -> encode_acc(Path, 0, 0, <<>>).
 2 
 3 % Private
 4 
 5 encode_acc([], _PLat, _PLng, Acc) -> Acc;
 6 encode_acc([{Lng, Lat}|Rest], PLat, PLng, Acc) ->
 7   LatE5 = round(Lat * 1.0e5),
 8   LngE5 = round(Lng * 1.0e5),
 9   EncodedLat = encode_part(encode_sign(LatE5 - PLat), <<>>),
10   EncodedLng = encode_part(encode_sign(LngE5 - PLng), <<>>),
11   encode_acc(Rest, LatE5, LngE5, <<Acc/binary, EncodedLat/binary, EncodedLng/binary>>).
12 
13 encode_sign(Num) when Num < 0 -> bnot (Num bsl 1);
14 encode_sign(Num) -> Num bsl 1.
15 
16 encode_part(Num, Result) when Num < 32 -> <<Result/binary, (Num + 63)>>;
17 encode_part(Num, Result) ->
18   Value = (32 bor (Num band 31)) + 63,
19   encode_part(Num bsr 5, <<Result/binary, Value>>).

The decode/1 function takes an encoded polyline and returns a path.

 1 decode(Line) -> decode_acc(Line, 0, 0, []).
 2 
 3 % Private
 4 
 5 decode_acc(<<>>, _Lat, _Lng, Acc) -> lists:reverse(Acc);
 6 decode_acc(Line, Lat, Lng, Acc) ->
 7   {DLat, Rest} = decode_part(Line, 32, 0, 0),
 8   Lat2 = Lat + DLat,
 9   {DLng, Rest2} = decode_part(Rest, 32, 0, 0),
10   Lng2 = Lng + DLng,
11   decode_acc(Rest2, Lat2, Lng2, [{Lng2 / 1.0e5, Lat2 / 1.0e5} | Acc]).
12 
13 decode_part(Line, B, _Shift, Result) when B < 32 ->
14   Result2 = if
15     Result band 1 == 0 -> Result bsr 1;
16     true -> bnot (Result bsr 1)
17   end,
18   {Result2, Line};
19 decode_part(<<C:8, Rest/binary>>, _OldB, Shift, Result) ->
20   B = C - 63,
21   Result2 = Result bor ((B band 31) bsl Shift),
22   decode_part(Rest, B, Shift + 5, Result2).

I have written these functions for noesis, which is a library that contains useful utility functions. Right now the implementation is only available in the development branch. It is tested using EUnit and QuickCheck.

If you’re reading this a couple of months from now, you might find an updated implementation on GitHub.

Comments