Draw path between two locations in google maps v2 xamarin android

In brief: Google maps android API V2 allows the developer to integrate and customize the google map look and feel. This API handles the access to Google Maps Server,fetching map data ,display and response to map gesture.
[read more : https://developers.google.com/maps/documentation/android/intro]
Here I will write the steps to draw path between two geo-location in android xamarin.

In my previous post shared my thought about  Best Practice and issues with ListView in Android XamarinHow to use Google Place API with Autocomplete in Xamarin AndroidIntegrating Login by Google Account in Xamarin.Android,How to avoid ImageBitmap OutOfMemoryException and Rounded corner Image in android Xamarin.

In detail :
To draw the custom path between two geo location,need to make use of the following map API component.
Markers : Icon on defined position
Polylines: Line connecting the defined position [reference: https://developers.google.com/maps/documentation/javascript/reference?hl=en#Polyline]
Polyline are created by passing PolylineOptions which intern specifies the path of the polyline and stroke style of the polyline.
PolylineOptions provides the important stroke style methods[reference : https://developers.google.com/maps/documentation/javascript/reference?hl=en#PolylineOptions]

In steps :
1. Integrate Google map,follow the this post[Build Google Map V2 in Xamarin Android]
GoogleMap map;
var frag = FragmentManager.FindFragmentById<mapfragment>(Resource.Id.map); 
 var mapReadyCallback = new OnMapReadyClass(); 
 mapReadyCallback.MapReadyAction += delegate(GoogleMap obj )
 {
  map=obj;  
  FnProcessOnMap();
 };   
 frag.GetMapAsync(mapReadyCallback);

 public class OnMapReadyClass :Java.Lang.Object,IOnMapReadyCallback
 { 
  public GoogleMap Map { get; private set; } 
  public event Action<googlemap> MapReadyAction;
  public void OnMapReady (GoogleMap googleMap)
  {
   Map = googleMap;
   if ( MapReadyAction != null )
    MapReadyAction (Map);
  }
 }
[Note: Avoid Gms.Maps.MapFragment.Map which is deprecated]

2.Register in Google developer console[https://console.developers.google.com]
2.1. In left menu select "APIs and Auth" and search for direction api and select and enable it.


2.2. In left menu select Credential. Click "Add credential"->"API key". In a popped up "create a new key" window,select "server key" and press "Create".



2.3. Note the server key created,same should be used in direction api request in below step.

3.Setup source and destination point.
Here i have taken two random location's,you can also provide auto-complete location using google api.
Convert that location into geo coordinates[lat,long]. This can be done in two ways using android native method[Geocoder()] or by using google geocoder api.

async Task<bool> FnLocationToLatLng()
  {
   try
   {
   var geo = new Geocoder ( this );
   var sourceAddress =await geo.GetFromLocationNameAsync(Constants.strSourceLocation , 1 );
   sourceAddress.ToList ().ForEach ((addr) => {
    latLngSource =new LatLng(addr.Latitude,addr.Longitude);
   } );

    var destAddress =await geo.GetFromLocationNameAsync(Constants.strDestinationLocation , 1 );
   destAddress.ToList ().ForEach ((addr) => {
    latLngDestination =new LatLng(addr.Latitude,addr.Longitude);
   } );

   return true;
   }
   catch
   {
    return false;
   }
  }


To use Google geocode api to convert location to coordinate follow this http://appliedcodelog.blogspot.in/2015/08/using-google-geocoding-and-reverse.html

3.Update the camera position.
To highlight the path properly,Need to zoom the map to the selected location. Here i will update camera to a source location.

 void FnupdateCameraPosition(LatLng pos)
  {
   CameraPosition.Builder builder = CameraPosition.InvokeBuilder();
   builder.Target(pos);
   builder.Zoom(12);
   builder.Bearing(45);
   builder.Tilt(10);
   CameraPosition cameraPosition = builder.Build();
   CameraUpdate cameraUpdate = CameraUpdateFactory.NewCameraPosition(cameraPosition);
   map.AnimateCamera(cameraUpdate);
  }

 4.Google direction API HttpWebRequest and parse the json response.
 Make a simple REST request [How to make REST request and JSON parsing] to google direction api along with the server key created in the step2. [read about the optional parameter and more https://developers.google.com/maps/documentation/directions/intro]
 Parse the JSON response to class object.

 internal static string strGoogleDirectionUrl="https://maps.googleapis.com/maps/api/directions/json?origin=src_locn&destination=dest_lcn&key=keyGoesHere";
 string strJSONDirectionResponse = await FnHttpRequest(strGoogleDirectionUrl);
 if ( strJSONDirectionResponse != Constants.strException )
   {
//mark source and destination point
  MarkOnMap(Constants.strTextSource,latLngSource,Resource.Drawable.MarkerSource);
  MarkOnMap(Constants.strTextDestination,latLngDestination,Resource.Drawable.MarkerDest);
   }
   var objRoutes = JsonConvert.DeserializeObject<googledirectionclass>( strJSONDirectionResponse );


  WebClient webclient;
  async Task<string> FnHttpRequest(string strUri)
  {
   webclient = new WebClient ();
   string strResultData;
   try
   {
    strResultData= await webclient.DownloadStringTaskAsync (new Uri(strUri));
    Console.WriteLine(strResultData);
   }
   catch
   {
    strResultData = Constants.strException;
   }
   finally
   {
    if ( webclient!=null )
    {
     webclient.Dispose ();
     webclient = null;
    }
   }

   return strResultData;
  }


  void MarkOnMap(string title,LatLng pos, int resourceId )
  {
   var marker = new MarkerOptions ();
   marker.SetTitle ( title );
   marker.SetPosition ( pos ); //Resource.Drawable.BlueDot
   marker.SetIcon ( BitmapDescriptorFactory.FromResource ( resourceId ) );
   map.AddMarker ( marker );
  }

 5.Decode the overview_polyline.points.
 For the security purpose google direction api returns the points along the path in a encoded type.
 Below used Decoding method returns the points in List of type Location. Which is need to be converted into Array of type LatLng to input to PolylineOptions.
 Here "objRoutes" may contain more then one possible route(objRoutes.routes.Count) between source and destination, I will take here the default one[i.e. index 0].

string encodedPoints = objRoutes.routes [0].overview_polyline.points;
 var lstDecodedPoints = FnDecodePolylinePoints ( encodedPoints );
 //convert list of location point to array of latlng type
 var latLngPoints = new LatLng[lstDecodedPoints.Count];
 int index = 0;
 foreach ( Location loc in lstDecodedPoints )
 {
  latLngPoints [index++] = new LatLng ( loc.lat , loc.lng );
 }

 //function to decode,encoded points
  List<location> FnDecodePolylinePoints(string encodedPoints)
  {
   if ( string.IsNullOrEmpty ( encodedPoints ) )
    return null;
   var poly = new List<location>();
   char[] polylinechars = encodedPoints.ToCharArray();
   int index = 0;

   int currentLat = 0;
   int currentLng = 0;
   int next5bits;
   int sum;
   int shifter;

    while (index < polylinechars.Length)
    {
     // calculate next latitude
     sum = 0;
     shifter = 0;
     do
     {
      next5bits = (int)polylinechars[index++] - 63;
      sum |= (next5bits & 31) << shifter;
      shifter += 5;
     } while (next5bits >= 32 && index < polylinechars.Length);

     if (index >= polylinechars.Length)
      break;

     currentLat += (sum & 1) == 1 ? ~(sum >> 1) : (sum >> 1);

     //calculate next longitude
     sum = 0;
     shifter = 0;
     do
     {
      next5bits = (int)polylinechars[index++] - 63;
      sum |= (next5bits & 31) << shifter;
      shifter += 5;
     } while (next5bits >= 32 && index < polylinechars.Length);

     if (index >= polylinechars.Length && next5bits >= 32)
      break;

     currentLng += (sum & 1) == 1 ? ~(sum >> 1) : (sum >> 1);
     Location p = new Location();
     p.lat = Convert.ToDouble(currentLat) / 100000.0;
     p.lng = Convert.ToDouble(currentLng) / 100000.0;
     poly.Add(p);
    }
 
   return poly;
  }

6.Instanitiate PolylineOptions to shape the path.
Input the latLngPoints created, here setting "Geodesic" true bring the path with more curved structure.
Add the polylineoption instance to map.

var polylineoption = new PolylineOptions ();
polylineoption.InvokeColor ( Android.Graphics.Color.Red );
polylineoption.Geodesic ( true );
polylineoption.Add ( latLngPoints );
map.AddPolyline ( polylineoption );

This is how we can draw a path between two geo-location.
Usage Limit: In the Google map direction document mentioned the below usage limit as of below date.
->2500 directions requests per 24 hour period.
->Up to 8 way points allowed in each request. Waypoints are not available for transit directions.
->2 requests per second

Explore the complete code at github:https://github.com/suchithm/DrawMapPathXamarin/ .

Hope you had liked this post & write your thoughts/suggestion if any below.

 Happiee coding,keep visiting :)

13 comments:

  1. Thanks, you helped me with a problem that tape-worm for weeks. And it wanted to know if it is possible that you help me with another topic, if I wanted to show the time of duration of the trip and the distance that this recorrera. And to show besides it them html_instructions.

    ReplyDelete
    Replies
    1. Glad it could helped you. In the same way for the distance,duration you can refer above GoogleDirectionClass field routes>legs >distance and for duration routes>legs>steps>html_instructions. happy coding!

      Delete
    2. Thank you and if I wanted to show the traffic that exists in the road?

      Delete
  2. Hi, i need your Help. My project when emuled on Android Device Asus Zenfone 5 and when i debug code on line

    " void FnSetDirectionQuery(string strJSONDirectionResponse)
    {
    var objRoutes = JsonConvert.DeserializeObject(strJSONDirectionResponse);"

    shows the following error: "{\n \"error_message\" : \"This IP, site or mobile application is not authorized to use this API key. Request received from IP address 177.205.115.189, with empty referer\",\n \"routes\" : [],\n \"status\" : \"REQUEST_DENIED\"\n}\n"



    As I fix this error? I have Server Key on console.google.com, added my IP, and continue this error, plese I need help!

    ReplyDelete
  3. IP is an optional, you can leave blank there because this implementation for client app.

    ReplyDelete
  4. Hi ,i have one problem App Running time FnLocationToLatLng() ,await geo.GetFromLocationNameAsync() this method not return async function,,terminate app..just showing blank map ..I am run app in Emulator ..Help me

    ReplyDelete
  5. Hi, can i know how to let user input their custom search address? i'm doing a application where let user to input the departure point and destination point, hope you will reply me =)

    ReplyDelete
  6. You are a life saver !!! :D Thank you so much!

    ReplyDelete
    Replies
    1. :) glad that it could helped you. Happy coding.

      Delete
  7. hi code is not working it wont show routes only map and points are showing

    ReplyDelete
  8. Hello from Brazil... here the lat come back as 7E-05 when I try do decode, do you know why?

    ReplyDelete
  9. Hi Suchith thanks for helpful blogs. For the above example how can I make user select one of the paths /routes available from direction API

    ReplyDelete