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: 
All content © Ruurd Idenburg, 2007–2025, 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 my on server at my home, falling under Dutch (privacy) laws.

This page updated on Wed, 28 May 2025 10:38:18 +0200.