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: 
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.