ooRexx logo
#!/usr/bin/env rexx
/* ---------------------------------------------------------------- */
/* Decodes a Google encoded PolyLine string into latitude/longitude */
/* pairs                                                            */
/* ---------------------------------------------------------------- */
/*                                                                  */
/* Originally by Ruurd J. Idenburg                                  */                                                            
/*                                                                  */
/* No copyright, no licence, no guarantees or warrantees, be it     */
/* explicit, implicit or whatever. Usage is totally and completely  */
/* at the users own risk, the author shall not be liable for any    */
/* damages whatsoever, for any reason whatsoever.                   */
/*                                                                  */
/* Please keep this comment block intact when modifying this code   */
/* and add a note with date and a description.                      */
/*                                                                  */
/* ---------------------------------------------------------------- */
/*  Parameter(s):                                                   */
/*                                                                  */
/*    encodedPolyline - a polyline encoded in Google format.        */
/*                      e.g. "_p~iF~ps|U_ulLnnqC_mqNvxq`@"          */
/*                                                                  */
/*  Result:                                                         */
/*                                                                  */
/*    If invoked as a command latitude/longitude pairs to stdout,   */
/*    if invoked as a function latitude/longitude pairs into array. */
/*                                                                  */
/* ---------------------------------------------------------------- */
/* 2013/12/13 - Initial version                                     */
/* 2014/10/15 - Improved deltalatitude/deltalongitude to prevent    */
/*              noticeable deviations in long encoded polylines     */
/* 2021/05/20 - Added hashbang #!/usr/bin/env rexx                  */
/* ---------------------------------------------------------------- */
--::routine decodeGString public
parse source os how me
if how=="FUNCTION" then myResult = .array~new()
numeric digits 20
 
parse arg encPoly
-- get rid of surrounding spaces
encPoly = encPoly~space(0)
-- get rid of surrounding quotes
if (encPoly~pos('"')=1 & encPoly~lastpos('"')=encPoly~length) then encPoly = encPoly~strip('B','"')
if (encPoly~pos("'")=1 & encPoly~lastpos("'")=encPoly~length) then encPoly = encPoly~strip('B',"'")
-- get rid of possibly escaped backslashes, may be needed in some cases
-- where the backslash is used as an escape character, e.g. javascript(?) strings
-- setup latitude and longitude
latitude = 0
longitude = 0
-- first position in encoded string
i = 1
do until i>=encPoly~length
  -- first the latitude
  result = '00000000'x
  byte = 32
  do j=1 by 1 while (byte>=32)
    -- Convert each character to it's decimal value minus 63
    byte = encPoly~subchar(i)~c2d-63
    -- bitwise AND byte with '1F'x then shift left 5*(j-1) (i.e. multiply by 32**(j-1)
    -- and OR into 4 byte result. That is: 5 bit chunks concatenated in reverse order.
    -- The opposite of the encoding steps.
    result = ((byte//32)*(32**(j-1)))~d2c(4)~bitor(result)
    -- increment position in encoded string
    i += 1
  end
  -- encoding used 2's complement for negative values
  -- if sign bit is not 0 then result should be negative
  if result~bitand('00000001'x)~c2d\=0
    then deltalatitude = -(result~c2d/2)~format(,0)/1e5  
    else deltalatitude = (result~c2d/2)~format(,0)/1e5
/*
    if result~bitand('00000001'x)~c2d\=0
    then testlatitude = latitude - ((result~c2d/2)~format(,0)/1e5)
    else testlatitude = latitude + ((result~c2d/2)~format(,0)/1e5)
*/

  latitude += deltalatitude
  -- now the longitude
  result = '00000000'x
  byte = 32
  do j=1 by 1 while (byte>=32)
    -- Convert each character to it's decimal value minus 63
    byte = (encPoly~subchar(i)~c2d-63)
    -- bitwise AND byte with '1F'x then shift left 5*(j-1) (i.e. multiply by 32**(j-1)
    -- and OR into 4 byte result
    result = ((byte//32)*(32**(j-1)))~d2c(4)~bitor(result)
    -- increment position in encoded string
    i += 1
  end
  -- if sign bit is not 0 then result should be negative
  if result~bitand('00000001'x)~c2d\=0
    then deltalongitude = -(result~c2d/2)~format(,0)/1e5
    else deltalongitude = (result~c2d/2)~format(,0)/1e5
/*
  if result~bitand('00000001'x)~c2d\=0
    then testlongitude = longitude - (result~c2d/2)~format(,0)/1e5
    else testlongitude = longitude + (result~c2d/2)~format(,0)/1e5
*/

  longitude += deltalongitude
  if how=="COMMAND"
    then say latitude longitude
    else myResult~append(latitude longitude)
end
if how=="FUNCTION" then return myResult


 
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.