ooRexx logo
   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: 
If you feel inclined to make corrections, suggestions etc., please mail me any.
All content © Ruurd Idenburg, 2007–, except where marked otherwise. All rights reserved. This page is primarily for non-commercial use only. The Idenburg website records no personal information and sets no ‘cookies’. This site is hosted on a VPS(Virtual Private System) rented from Transip.nl, a Dutch company, falling under Dutch (privacy) laws (I think).

This page updated on by Ruurd Idenburg.