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