Android Example MapsDecodePolyline

Hello!

As already told in another thread, i'm working on a navigation app using Google Maps API in combination with Google Directions http-requests for routing.

The http-Requests looks as following:

http://maps.googleapis.com/maps/api...1,16.324900053441525&sensor=false&language=de

As origin parameter i use the current gps data of the android device and as destination parameter i use the gps coordinates of one poi on the map.

The result is formatted in json.

After decoding json with the json-library i decode polyline points and store them in lMapsPoints list.

So i'm able to draw the route to maps:

maps.png


You'll find some implementations in internet to this topic - but they are all written in C++. So i used this code and transferred it to b4a. There are a lot of bit-operations. Input is the polyline string and output is a list of MapsPointItem-elements:

B4X:
Sub MapsDecodePolyline(encoded As String, poly As List)
    Log("MapsDecodePolyline")
   
    Dim index As Int
    Dim lat As Int
    Dim lng As Int
    Dim fLat As Float
    Dim fLng As Float
    Dim b As Int
    Dim shift As Int
    Dim result As Int
    Dim dlat As Int
    Dim dlng As Int
    Dim p As LatLng
    Dim i As Int
    Dim l As LatLng

    index = 0 : lat = 0 : lng = 0
    Do While index < encoded.Length
        shift = 0 : result = 0
        Do While True
            b = Asc(encoded.SubString2(index, index + 1)) - 63 : index = index + 1
            result = Bit.OR(result, Bit.ShiftLeft(Bit.AND(b, 0x1f), shift))
            shift = shift + 5
            If b < 0x20 Then Exit
        Loop
       
        If Bit.AND(result, 1) = 1 Then
            dlat = Bit.Not(Bit.ShiftRight(result, 1))
        Else
            dlat = Bit.ShiftRight(result, 1)
        End If
        lat = lat + dlat
        shift = 0 : result = 0
        Do While True
            b = Asc(encoded.SubString2(index, index + 1)) - 63 : index = index + 1
            result = Bit.OR(result, Bit.ShiftLeft(Bit.AND(b, 0x1f), shift))
            shift = shift + 5
            If b < 0x20 Then Exit
        Loop
       
        If Bit.AND(result, 1) = 1 Then
            dlng = Bit.Not(Bit.ShiftRight(result, 1))
        Else
            dlng = Bit.ShiftRight(result, 1)
        End If
        lng = lng + dlng
        fLat = lat
        fLng = lng
       
        Dim mpiCurrent As MapsPointItem
        mpiCurrent.Initialize
        mpiCurrent.iPassed = 0
        mpiCurrent.dLatitude = fLat / 100000
        mpiCurrent.dLongitude = fLng / 100000
        poly.add(mpiCurrent)
    Loop
End Sub


B4X:
Type MapsPointItem (dLatitude As Double, dLongitude As Double, iPassed As Int)

Drawing the route is very easy:

B4X:
MapsDecodePolyline(sPolyline, lMapsPoints)
   
Dim i As Int
Dim llPoint As LatLng
Dim points As List
Dim mpiCurrent As MapsPointItem

bMapsRoutingInProgress = True
bMapsManeuverPassed = False
Main.bDoNotDisturb = True
points.Initialize

For i = 0 To lMapsPoints.Size - 1
  mpiCurrent = lMapsPoints.Get(i)
  llPoint.Initialize(mpiCurrent.dLatitude, mpiCurrent.dLongitude)
  points.Add(llPoint)
Next

Dim pl As Polyline = gmap.AddPolyline
pl.points = points
pl.Color = utilities.v4_Colors("holo_blue_light")

Feel free to use it!

Martin
 

jhT

New Member
Licensed User
Longtime User
Thanks very much for posting this - I managed to link this up to my code and it worked a treat.

Do you automatically zoom to the size of the overall route too?
 

me68

Member
Licensed User
Longtime User
Thanks very much for posting this - I managed to link this up to my code and it worked a treat.

Do you automatically zoom to the size of the overall route too?

No. But if you figure out something, please let me know.

Martin
 

jhT

New Member
Licensed User
Longtime User
I have managed to get automatic zoom using the AnimateToBounds method in the GoogleMapsExtras library.

If you use the list of points generated by the MapsDecodePolyline sub, you can work out the Lat/Lng boundaries and then apply them to the map

B4X:
Sub SetMapZoom(PointsList As List)

    'Zoom to centre and include the lat/lng boundaries given by the route

    Dim gmE As GoogleMapsExtras
    Dim llBounds As LatLngBounds
    Dim llNE As LatLng, llSW As LatLng, llPoint As LatLng
    Dim dblNELat As Double, dblNELng As Double, dblSWLat As Double, dblSWLng As Double
    Dim i As Int
   
    'Initialise points
    llPoint = PointsList.Get(0)
    dblNELat = llPoint.Latitude
    dblNELng = llPoint.Longitude
    dblSWLat = llPoint.Latitude
    dblSWLng = llPoint.Longitude
   
    'Go through all points to get greatest and smallest
    For i = 1 To PointsList.Size -1
        llPoint = PointsList.Get(i)
        If dblNELat < llPoint.Latitude Then dblNELat = llPoint.Latitude
        If dblNELng < llPoint.Longitude Then dblNELng = llPoint.Longitude
        If dblSWLat > llPoint.Latitude Then dblSWLat = llPoint.Latitude
        If dblSWLng > llPoint.Longitude Then dblSWLng = llPoint.Longitude
    Next
 
    'Set the Lat/Lng boundaries
    llNE.Initialize(dblNELat, dblNELng)
    llSW.Initialize(dblSWLat, dblSWLng)
    llBounds.Initialize(llSW,llNE)
   
    'Perform animation on google map (pnlMap is the panel containing the Google map)
    gmE.AnimateToBounds2(gmap, llBounds, pnlMap.Width, pnlMap.Height, 40)

End Sub

Hope this is useful to you.

Jonathan
 

warwound

Expert
Licensed User
Longtime User
The new version of GoogleMapsExtras will contain the LatLngBounds.Builder object thar will simplify your posted code.

Contact me if you want a preview version to test.

Martin.
 

marcick

Well-Known Member
Licensed User
Longtime User
Hi, I have used your code to decode polyline from Google direction api, thanks.
I would like to know if you had success in writing a good navigation system with voice guidance.
I need to implement it in my app and don't want to use intent to call the external navigator.
 

claude330

Member
Licensed User
Longtime User
I have used your code to decode polyline from Google directions. It works fine. Thanks very much.
 

Carlos Bernardino

New Member
Licensed User
Longtime User
The result is formatted in json.

After decoding json with the json-library i decode polyline points and store them in lMapsPoints list.
Martin, could you please tell me how to decode the result of the Google Directions http request with the json-library? Thank you.
 

Petrovic

Member
Licensed User
Longtime User
I tried to use your code but can you please explain me something?
I used httputils2 lib and job.download to get and store google direction json in a string. After that I used that stiring as encoded parameter
in your MapsDecodePolyline function to decode it and put points in lMapsPoints list but I got an error when calling MapsDecodePolyline function
when it comes to this part:
b = Asc(encoded.SubString2(index, index + 1)) - 63 : index = index + 1
It gives me following error:
java.lang.StringIndexOutOfBoundsException: length=8933; regionStart=8933; regionLength=1

I have tried in couple of days to find a solution but no luck.
Can you please help me solve this problem and guide me in right direction.
 

Petrovic

Member
Licensed User
Longtime User
I solved it, made a misprint in json parsing, sorry :)
 

Luiz Fernando Orlandini

Active Member
Licensed User
Longtime User
Hello All!

I had implemented this routine with success.

In my case, I have to redraw the polylines every 10 seconds. In the first execution the route is draw perfectly. But after the second time, one line is draw, making a connection from initial to end point.

Anybody can help me to solve this?

Regards.
 

marcick

Well-Known Member
Licensed User
Longtime User
Do you clear the polyline before redrwaing it ?

something like this:

B4X:
If Pline.IsInitialized=True Then
        Points.Clear
        Pline.Points=Points
    End If
 

Nikita Mae Tuanquin

New Member
Licensed User
Longtime User
I tried to use your code but can you please explain me something?
I used httputils2 lib and job.download to get and store google direction json in a string. After that I used that stiring as encoded parameter
in your MapsDecodePolyline function to decode it and put points in lMapsPoints list but I got an error when calling MapsDecodePolyline function
when it comes to this part:

It gives me following error:


I have tried in couple of days to find a solution but no luck.
Can you please help me solve this problem and guide me in right direction.

Hi. I know it's been so long since you posted this but.. I also encountered this error.. Just like you, I obtained my google direction JSON data using httputils2 and fed that string to the MapsDecodePolyline function.. Do you happen to still remember what you did with the JSON parsing? I've been stuck with this problem for a few days..
 

Mikonios

Active Member
Licensed User
Longtime User
For ::::
MiUrl = "http://maps.googleapis.com/maps/api...on=39.50758,-3.52579&sensor=False&language=de"

Error in ::::
b = Asc(encoded.SubString2(index, index + 1)) - 63 : index = index + 1

It gives me following error:
java.lang.StringIndexOutOfBoundsException: length=15414; regionStart=15414; regionLength=1

I have tried in couple of days to find a solution but no luck.
Can you please help me solve this problem and guide me in right direction.

See Attach Image!!!!
 

Attachments

  • Error.png
    Error.png
    58.2 KB · Views: 598

Mikonios

Active Member
Licensed User
Longtime User
Sorry.
I solved it, made a XML parsing.

B4X:
Sub GeneraRuta()
   
    wm = ""
    Dim MiUrl As String
    Dim request As HttpRequest
    Dim MiTaskId As String
   
    Dim MiFechaHora, MiFecha As String
    DateTime.DateFormat = "yyyy-MM-dd HH:mm:ss"               
    MiFechaHora = DateTime.Now
       
    If CheckConnection Then
        url = "http://maps.googleapis.com/maps/api/directions/json?origin=48.267058,16.425503&destination=48.13769992466281,16.324900053441525&sensor=false&language=de"
        MiUrl = "http://maps.googleapis.com/maps/api/directions/xml?origin=40.38404,-3.69250&destination=39.50758,-3.52579&sensor=False&language=es"
'        MiUrl = MiUrl.Replace("LATI", Latitud)
'        MiUrl = MiUrl.Replace("LONG", Longitud)
       
        request.InitializeGet(MiUrl)
        request.Timeout = 10000 'set timeout to 10 seconds
       
        If HttpClient1.Execute(request, MiFechaHora) = False Then
            Return 'Will be false if there is already a running task (with the same id).
        Else
            'HttpClient1_ResponseSuccess (Response As HttpResponse, TaskId As Int)   
        End If
        ProgressDialogShow("Calling address server...")
       
    End If
   
End Sub

Sub HttpClient1_ResponseSuccess (Response As HttpResponse, TaskId As Int)
   
    Dim SQL1 As SQL
    Dim Query As String
   
       ProgressDialogHide
       Dim result As InputStream
    Try
           result = Response.GetInputStream 'GetString("UTF8") 'Convert the response to a string
    Catch
        Log("Error ::: Response.GetInputStream ")
    End Try
    '--------------------------------------------------------------------------------------------------------------
    Response.GetAsynchronously("Response", File.OpenOutput(Main.DirCcDta, TaskId & "wm.txt", False), True, TaskId)
    '--------------------------------------------------------------------------------------------------------------
   
       Log("ResponseSuccess")
       Log(result)
   
    MiListaPoly.Initialize
       Parser.Parse(result, "Parser")
    MiListaPoly = MiListaPoly
    wm = ""
  
       result.Close
   
End Sub

Sub HttpClient1_ResponseError (Reason As String, StatusCode As Int, TaskId As Int)
    Log(Reason)   
    Log(StatusCode)
    ProgressDialogHide
    If StatusCode = -1 Then msg = "The phone is offline"
    ToastMessageShow (Reason, True)
End Sub

Sub Response_StreamFinish (Success As Boolean, TaskId As Int)
    Dim another_buffer () As Byte
    another_buffer = my_buffer.ToBytesArray
    Log (BytesToString(another_buffer, 0, another_buffer.Length, "UTF8"))
End Sub

Sub Parser_StartElement (Uri As String, Name As String, Attributes As Attributes)

End Sub

Sub Parser_EndElement (Uri As String, Name As String, Text As StringBuilder)
   'http://maps.google.com/maps/api/geocode/xml?latlng=40.47981437,-3.37913171&sensor=false
    If Name = "points" Then
        If wm.Length > 0 Then Return
            wm = "" & Text.ToString & CRLF
            MapsDecodePolyline(Text.ToString, MiListaPoly)
            wm = ""
      End If
End Sub

Sub CheckConnection As Boolean
' If MyLan.IsInitialized Then WifiOn = CheckWiFi
' CheckConnection
Dim p AsPhone
If (p.GetDataState == "CONNECTED") Then
ReturnTrue
EndIf
If (p.GetSettings ("wifi_on") == 1) Then
ReturnTrue
EndIf
If (p.GetDataState == "DISCONNECTED") Then
ReturnFalse
EndIf
If (p.GetDataState == "SUSPENDED") Then
ReturnFalse
EndIf
End Sub
 

BarryW

Active Member
Licensed User
Longtime User
Hello!

As already told in another thread, i'm working on a navigation app using Google Maps API in combination with Google Directions http-requests for routing.

The http-Requests looks as following:

http://maps.googleapis.com/maps/api...1,16.324900053441525&sensor=false&language=de

As origin parameter i use the current gps data of the android device and as destination parameter i use the gps coordinates of one poi on the map.

The result is formatted in json.

After decoding json with the json-library i decode polyline points and store them in lMapsPoints list.

So i'm able to draw the route to maps:

maps.png


You'll find some implementations in internet to this topic - but they are all written in C++. So i used this code and transferred it to b4a. There are a lot of bit-operations. Input is the polyline string and output is a list of MapsPointItem-elements:

B4X:
Sub MapsDecodePolyline(encoded As String, poly As List)
    Log("MapsDecodePolyline")
  
    Dim index As Int
    Dim lat As Int
    Dim lng As Int
    Dim fLat As Float
    Dim fLng As Float
    Dim b As Int
    Dim shift As Int
    Dim result As Int
    Dim dlat As Int
    Dim dlng As Int
    Dim p As LatLng
    Dim i As Int
    Dim l As LatLng

    index = 0 : lat = 0 : lng = 0
    Do While index < encoded.Length
        shift = 0 : result = 0
        Do While True
            b = Asc(encoded.SubString2(index, index + 1)) - 63 : index = index + 1
            result = Bit.OR(result, Bit.ShiftLeft(Bit.AND(b, 0x1f), shift))
            shift = shift + 5
            If b < 0x20 Then Exit
        Loop
      
        If Bit.AND(result, 1) = 1 Then
            dlat = Bit.Not(Bit.ShiftRight(result, 1))
        Else
            dlat = Bit.ShiftRight(result, 1)
        End If
        lat = lat + dlat
        shift = 0 : result = 0
        Do While True
            b = Asc(encoded.SubString2(index, index + 1)) - 63 : index = index + 1
            result = Bit.OR(result, Bit.ShiftLeft(Bit.AND(b, 0x1f), shift))
            shift = shift + 5
            If b < 0x20 Then Exit
        Loop
      
        If Bit.AND(result, 1) = 1 Then
            dlng = Bit.Not(Bit.ShiftRight(result, 1))
        Else
            dlng = Bit.ShiftRight(result, 1)
        End If
        lng = lng + dlng
        fLat = lat
        fLng = lng
      
        Dim mpiCurrent As MapsPointItem
        mpiCurrent.Initialize
        mpiCurrent.iPassed = 0
        mpiCurrent.dLatitude = fLat / 100000
        mpiCurrent.dLongitude = fLng / 100000
        poly.add(mpiCurrent)
    Loop
End Sub


B4X:
Type MapsPointItem (dLatitude As Double, dLongitude As Double, iPassed As Int)

Drawing the route is very easy:

B4X:
MapsDecodePolyline(sPolyline, lMapsPoints)
  
Dim i As Int
Dim llPoint As LatLng
Dim points As List
Dim mpiCurrent As MapsPointItem

bMapsRoutingInProgress = True
bMapsManeuverPassed = False
Main.bDoNotDisturb = True
points.Initialize

For i = 0 To lMapsPoints.Size - 1
  mpiCurrent = lMapsPoints.Get(i)
  llPoint.Initialize(mpiCurrent.dLatitude, mpiCurrent.dLongitude)
  points.Add(llPoint)
Next

Dim pl As Polyline = gmap.AddPolyline
pl.points = points
pl.Color = utilities.v4_Colors("holo_blue_light")

Feel free to use it!

Martin

Can you post a running sample for this... tnx
 

SCIS

Active Member
Licensed User
Longtime User
Do you need libraries for this? I can't seem to find them. If not, I've no idea how I should start on this since I'm new to b4a
 
Top