Saturday, June 25, 2011

How to show turn by turn directions on Android

As you can probably make out, these days I am working on an Android project.  And I will tell you one thing, I am loving it!
I have worked on iOS, BREW and Simbian OS in the past but the productivity, while developing on Android is far better than the other platforms!

Excellent job Android guys!

Coming back to the post topic, recently I in one of the Android projects, we had a requirement that we wanted to show turn by turn directions between two geo points.

We are all familier with the excellent support for Turn by turn directions on the native Google Maps application.

Although, Android API permits us embed a maps view in the application, but it does not provide a way to show turn by turn directions between two geo points.

In early days of Android (SDK 1.0), Google had provided the DrivingDirections class.  Using this class it was possible to get the driving directions between two geo points.  However, since SDK 1.1, Google removed the DrivingDirections class from the API. 

Why would they remove something as useful as this?

Because it violated some of the legal agreements made by Google.  Extract from the Google Maps terms states that
8.3 Content License. Subject to these Terms (including but not limited to Section 9 (License Requirements) and Section 10 (License Restrictions)), Google gives you a personal, worldwide, royalty-free, non-transferable, non-assignable, and non-exclusive license to access, use, publicly perform and publicly display the Content in your Maps API Implementation, as the Content is provided in the Service, and in the manner permitted by the Terms. Specifically, you understand the following:


(a) Content (including but not limited to map data, traffic, directions, and places) is provided for planning purposes only. You may find that weather conditions, construction projects, closures, or other events may cause road conditions or directions to differ from the results depicted in the Content. You should exercise judgment in your use of the Content.
(b) Certain Content is provided under license from third parties, including Tele Atlas B.V. ("Tele Atlas"), and is subject to copyright and other intellectual property rights owned by or licensed to Tele Atlas and/or such third parties. You may be held liable for any unauthorized copying or disclosure of this content. Your use of Tele Atlas map data and certain other Content (including certain business listings Content) is subject to additional restrictions located in the Legal Notices page.

 In simple terms, Google has tied up with many Third party companies to get the mapping and other information.  The terms of the agreement do not allow them to expose those functionality in an open source project.  This is the reason why they removed the DrivingDirections API since SDK 1.1

All right fine!  Google could not expose that as API's, but we sill have this requirement to fulfill.  We want to show turn by turn directions between two geo points on Android platform.

How do we do it?

As we all know that the native Google Maps application (which comes pre-installed on all Android devices) does a great job of showing Turn by Turn driving directions.  The updated application also provides a link to Google Navigate (beta) which shows 3D maps and updates the route as the user location changes.

We should definitely try and make use of this feature for our requirement.  After all, why reinvent the wheel!

Hence now, our tasks are:
  • To invoke the native google maps application
  • Show turn by turn directions between two geo points on the native google maps application
One very big plus of the Android platform is that, it has multitasking support.  Because of this, when user clicks the back button on the phone, they will come back to our application, user does not feel they are navigating away from our application.  This enhances the user experience quite a lot!

So the questions is how do we invoke the native Google Maps application to show turn by turn direction between two geo points!

Inter process/activity communication on Android platform is done via an Intent.  Hence the answer to our question is: Via an Intent!

Yes and that's the correct answer!  But what Intent, what will be the URL, what should be the parameters?

Slow down, slow down, lets tackle one problem at a time!

This is how they do it!

Looking at this URL it seems that we can invoke the native maps application using the following URLs

One line description for the above url's is
Opens the Maps application to the given location or query. The Geo URI scheme (not fully supported) is currently under development.
The z field specifies the zoom level. A zoom level of 1 shows the whole Earth, centered at the given lat,lng. A zoom level of 2 shows a quarter of the Earth, and so on. The highest zoom level is 23. A larger zoom level will be clamped to 23.
Basically, the description says that, The above URLs will show the maps application which is centered around the given geo point

Hence,

This code will actuall open up the native map application which is centered around the given geo point. We have solved one task from our task list. We have managed to invoke the native google maps application!

But wait!  We want to show two points not just one point on the map.  We also want to show turn by turn directions between those points!

Using the URL i.e. the geo:x:y in the above format, we cannot specify two geo points.  Damn!

I tried many permutations & combinations to specify the second geo point, but nothing worked.

Finally, after a lot of trials and errors I found a workaround to this problem.

This is how its actually done!

The URL geo:x,y does not support the second geo point, hence we can't use that URL.  What is the other URL we could use, which will show turn by turn directions between two geo points?

After some more googling, I found that, we might be able to use the URL format used by  maps.google.com website.  The maps.google.com follows a simple url pattern

The maps.google.com uses two parameters "saddr" (source address) and "daddr" (destination address). If we pass those two parameters correctly it shows, turn by turn directions between those points.
Web Based Turn-By-Turn Directions on Google Maps

Why are we even bothered about viewing turn by turn directions on the web?

Because guess what, we can use the same web based URL pattern on the Android platform!

What?  Yes, we can use the URL http://maps.google.com/maps?saddr=43.0054446,-87.9678884&daddr=42.9257104,-88.0508355 to open up the Google Maps application to see turn by turn directions between two geo points!

The above code surely works! It shows the user, turn by turn directions between the geo points [43.0054446,-87.9678884] and [42.9257104,-88.0508355].

There is only one problem though, Android platform identifies that the above URL can be processed by two Activities!
  • The Browser Activity - Since the URL starts with http:// 
  • The Google Maps Activity - Since the address we are opening up is maps.google.com
Whenever Android platform has more than one options to process an intent, it leaves the choice to users, gives them an option of both the applications and lets them chose the application to fulfill the intent.
User gets an option to chose the application for the processing the Maps URL
User can chose either application, both show driving directions between those two geo points.

Driving directions using the browser application

Driving directions using the Native Google Maps application

How to make this solution better:

Although we have achieved what we intended for.  But there is one extra step that user has to perform, i.e. select the application of his choice to see the driving directions.  At this point, user might notice that he is going out of the current application and into a different application.  There is nothing wrong with this approach but, it does not integrate the Google Maps application seamlessly with our application.

How do we eliminate this extra step of choosing the application to view turn by turn directions?

The answer is simple and we can do this by providing a hint in the Intent we are firing.

The line intent.setClassName("com.google.android.apps.maps", "com.google.android.maps.MapsActivity"); does the trick. We are passing additional information in the Intent. We are informing that, the class name of the activity that should fulfill the intent is "com.google.android.apps.maps/com.google.android.maps.MapsActivity"

That's it! Now Android platform knows that, user wants to see the turn by turn directions on the native google maps application and not on the browser. The extra step of chosing the application is eliminated.

User directly goes to this view
There are many advantage of this approach
  • User can choose to start navigation using Google Navigate (beta) (without us doing anything).
  • User can click back button and return to our application as well!
As we can see, Android platform is pretty awesome, when it comes to ease of development!  Something as complex as showing turn by turn driving directions between two geo points can be achieved by writing 3 lines of code!

31 comments:

  1. Hey,

    Thanks for the tutorial! I'm trying to get the path duration and total distance instead of a turn by turn method. How do I access the duration and total distance?

    Thanks

    ReplyDelete
  2. Hello Soto,

    You can calculate the total distance between two geo points using the Location Api itself http://developer.android.com/reference/android/location/Location.html#distanceBetween%28double,%20double,%20double,%20double,%20float[]%29

    There are methods in the Location class to do this.

    I am not sure about duration however. Duration is very dynamic information. It would change based on a lot of factors. Traffic conditions, weather conditions etc.

    In the current post I have explained how to open up the native application for doing the job. If you open up the native Google Navigate application then it would show duration for you.

    Thanks

    Deep Shah

    ReplyDelete
  3. hey nice tutorial,
    but i have little problem
    did you know how to get directions from my current gps location to the geopoints insert manually like create above i really need some help
    thanks

    ReplyDelete
  4. Hello Don,

    I am a bit confused about the question. Do you want to find your current location and then want to show directions between your location and some other geo point? If yes then you can get the users location using the LocationListener [http://developer.android.com/reference/android/location/LocationListener.html] API. And then you can use the method described above to show directions between two geo points.

    If your question is something else please can you explain the question again?

    ReplyDelete
  5. Hello very nice tutorial , is it possible that source address can be selected automatically from Gps . Actually i want to draw the line between the given address and my current location .i am trying to solve in this way but dont know how to draw line between them

    String loc ="";
    String city ="";
    String address = loc + city
    + "";
    String cleanAddress = address.replace(",", "");
    cleanAddress = cleanAddress.replace(' ', '+');

    try {
    Intent geoIntent = new Intent("android.intent.action.VIEW", android.net.Uri.parse("geo:0,0?q="
    + cleanAddress));

    startActivity(geoIntent);
    } catch (Exception ee) {
    Log.d("CH12", "error launching map? " + ee.getMessage());
    }

    I will be looking froward for your reply

    ReplyDelete
  6. I don't think there is a way that you can specify the Native Google maps to use the current GPS location. You will have to pass it as one of the arguments to the http://maps.google.com url as shown in the post.

    ReplyDelete
  7. Hi,
    Great tutorial!!
    Question:Is it possible to add markers like a push pin to the map on particular location?

    ReplyDelete
  8. Sumi

    hi,
    This is for ohad.....
    Yes it is possible to add marker by using ItemizedOverlay Class.

    Just Look at this site:http://mobiforge.com/developing/story/using-google-maps-android

    ReplyDelete
  9. Nice tutorial It works but i would lik eto give more place instead of two can u help me as i am new to android google maps

    ReplyDelete
  10. Hello There,

    Since the technique I am using uses Google Maps Url, you can use all the parameters that can be passed to Google Maps via URL.

    Here is the link to the full set of parameters that can be passed via URL to Google Maps

    http://mapki.com/wiki/Google_Map_Parameters

    Thanks

    Deep Shah

    ReplyDelete
  11. hi Deep nice tutorial,, i have one problem i can set my destination address dynamically, but i am getting problem in finding the current location(lat & lon) of device. i m using LocationListener but giving me null result, can u share something useful regarding will be lot help full.


    Thank You-

    ReplyDelete
  12. hi , a great tut. I am wondering how to keep update route between source and destination when current location keeps changing. Any idea? Right now, I am using uri to show the route between 2 geopoints. But how do i update the route when current location is changed ?

    ReplyDelete
  13. tnx man...........

    ReplyDelete
  14. @Deep Shah wonderful tutorial... could you let me know if it is anywhere possible to load maps and MapActivity class with in our layout.

    ReplyDelete
  15. Hello There,

    In the URL approach, the native maps application is used to show the turn by turn route. Hence, you do not need to update the map if user changes his location. The Maps application has options to update the map automatically when users changes his location.

    Thanks

    Deep Shah

    ReplyDelete
  16. Hi, how about licensing? Can i use this king of invocation in commercial application?

    ReplyDelete
  17. Hello,,
    I have tried to run in emulator and it works, but when i put on my phone always display 'force close'. What is the slolution

    ReplyDelete
  18. This feature would really be useful, it just sounds a bit tricky to setup. Hopefully I'll be able to get it up and running on my phone.

    ReplyDelete
  19. Hi, Is there any way to set the map to 'walking directions' by default, instead of 'driving directions'?
    Thanks

    ReplyDelete
  20. This is a very comprehensive tutorial about Google maps. I usually use the Google Maps on my Android phone to search for the nearest place. I find Google Maps very informative too. I'm glad I'm on a data plan so I can use data services anytime and anywhere.

    ReplyDelete
  21. Can we add buttons or textboxes in the same window where map is displayed. If yes, how? Please explain...

    ReplyDelete
  22. I want to set source address as my current location how can i set it...
    if using Gps the how would i pass this co-ordinates to URL of "&saddr"...
    PLz Reply .....

    ReplyDelete
  23. Thanks. Any idea about plotting or getting all suggested route between two points. By using the map api, it only gives 1 route.

    Avik.

    ReplyDelete
  24. Hi Deep,

    I am also trying to find turn by turn directions in my applications and I already have a set of coordinates between, say, a point A and a point B. I want to find turn by turn directions between those intermediate coordinates from A to reach at B. Its not an Android application. So could you please tell me how to use your logic?

    ReplyDelete
  25. Hi,
    Great Tutorial.

    Is it possible to get directions list between two places directly without showing map view?

    Thanks in advance.

    ReplyDelete
  26. Hi
    good tutorial to learn.

    actually in my case i hv to provide via places selected by user so i use link like following : https://maps.google.com/maps?saddr=new+york+to:baltimore+to:philadelphia+to:cleveland

    But it gives me turns list first & to view map i hv to manually select view map.. any hint hw can i show that map directly

    Thanx

    ReplyDelete
  27. Thank deep just what I was looking for, great tutorial, turn by turn directions are showing up just as they should.

    ReplyDelete
  28. Excellent read, I just passed this onto a colleague who was doing a little research on that. And he actually bought me lunch because I found it for him smile So let me rephrase that.

    Blackberry Unlock

    ReplyDelete
  29. Thank you, it is very useful but it possible to i m modify this map? i m using below example for get direction but whenever i tap on hardware back but it can't take up me in my app on one tap, but it take up me in my app on two tap. can i go in my app on back button one tap?
    Uri.parse("http://maps.google.com/maps?" +
    "saddr=43.0054446,-87.9678884" +
    "&daddr=42.9257104,-88.0508355"));
    intent.setClassName(
    "com.google.android.apps.maps",
    "com.google.android.maps.MapsActivity");
    startActivity(intent);

    ReplyDelete
  30. Thanks a lot.Never thought this would be such an easy task.

    ReplyDelete

Have some Fun!