1: #!/usr/bin/env rexx
2: /* ---------------------------------------------------------------- */
3: /* Decodes a Google encoded PolyLine string into latitude/longitude */
4: /* pairs */
5: /* ---------------------------------------------------------------- */
6: /* */
7: /* Originally by Ruurd J. Idenburg */
8: /* */
9: /* No copyright, no licence, no guarantees or warrantees, be it */
10: /* explicit, implicit or whatever. Usage is totally and completely */
11: /* at the users own risk, the author shall not be liable for any */
12: /* damages whatsoever, for any reason whatsoever. */
13: /* */
14: /* Please keep this comment block intact when modifying this code */
15: /* and add a note with date and a description. */
16: /* */
17: /* ---------------------------------------------------------------- */
18: /* Parameter(s): */
19: /* */
20: /* encodedPolyline - a polyline encoded in Google format. */
21: /* e.g. "_p~iF~ps|U_ulLnnqC_mqNvxq`@" */
22: /* */
23: /* Result: */
24: /* */
25: /* If invoked as a command latitude/longitude pairs to stdout, */
26: /* if invoked as a function latitude/longitude pairs into array. */
27: /* */
28: /* ---------------------------------------------------------------- */
29: /* 2013/12/13 - Initial version */
30: /* 2014/10/15 - Improved deltalatitude/deltalongitude to prevent */
31: /* noticeable deviations in long encoded polylines */
32: /* 2021/05/20 - Added hashbang #!/usr/bin/env rexx */
33: /* ---------------------------------------------------------------- */
34: --::routine decodeGString public
35: parse source os how me
36: if how=="FUNCTION" then myResult = .array~new()
37: numeric digits 20
38:
39: parse arg encPoly
40: -- get rid of surrounding spaces
41: encPoly = encPoly~space(0)
42: -- get rid of surrounding quotes
43: if (encPoly~pos('"')=1 & encPoly~lastpos('"')=encPoly~length) then encPoly = encPoly~strip('B','"')
44: if (encPoly~pos("'")=1 & encPoly~lastpos("'")=encPoly~length) then encPoly = encPoly~strip('B',"'")
45: -- get rid of possibly escaped backslashes, may be needed in some cases
46: -- where the backslash is used as an escape character, e.g. javascript(?) strings
47: -- setup latitude and longitude
48: latitude = 0
49: longitude = 0
50: -- first position in encoded string
51: i = 1
52: do until i>=encPoly~length
53: -- first the latitude
54: result = '00000000'x
55: byte = 32
56: do j=1 by 1 while (byte>=32)
57: -- Convert each character to it's decimal value minus 63
58: byte = encPoly~subchar(i)~c2d-63
59: -- bitwise AND byte with '1F'x then shift left 5*(j-1) (i.e. multiply by 32**(j-1)
60: -- and OR into 4 byte result. That is: 5 bit chunks concatenated in reverse order.
61: -- The opposite of the encoding steps.
62: result = ((byte//32)*(32**(j-1)))~d2c(4)~bitor(result)
63: -- increment position in encoded string
64: i += 1
65: end
66: -- encoding used 2's complement for negative values
67: -- if sign bit is not 0 then result should be negative
68: if result~bitand('00000001'x)~c2d\=0
69: then deltalatitude = -(result~c2d/2)~format(,0)/1e5
70: else deltalatitude = (result~c2d/2)~format(,0)/1e5
71: /*
72: if result~bitand('00000001'x)~c2d\=0
73: then testlatitude = latitude - ((result~c2d/2)~format(,0)/1e5)
74: else testlatitude = latitude + ((result~c2d/2)~format(,0)/1e5)
75: */
76: latitude += deltalatitude
77: -- now the longitude
78: result = '00000000'x
79: byte = 32
80: do j=1 by 1 while (byte>=32)
81: -- Convert each character to it's decimal value minus 63
82: byte = (encPoly~subchar(i)~c2d-63)
83: -- bitwise AND byte with '1F'x then shift left 5*(j-1) (i.e. multiply by 32**(j-1)
84: -- and OR into 4 byte result
85: result = ((byte//32)*(32**(j-1)))~d2c(4)~bitor(result)
86: -- increment position in encoded string
87: i += 1
88: end
89: -- if sign bit is not 0 then result should be negative
90: if result~bitand('00000001'x)~c2d\=0
91: then deltalongitude = -(result~c2d/2)~format(,0)/1e5
92: else deltalongitude = (result~c2d/2)~format(,0)/1e5
93: /*
94: if result~bitand('00000001'x)~c2d\=0
95: then testlongitude = longitude - (result~c2d/2)~format(,0)/1e5
96: else testlongitude = longitude + (result~c2d/2)~format(,0)/1e5
97: */
98: longitude += deltalongitude
99: if how=="COMMAND"
100: then say latitude longitude
101: else myResult~append(latitude longitude)
102: end
103: if how=="FUNCTION" then return myResult
104:
105: