Routing / Driving directions on Android – Part 2: Draw the route
After you got the route from wherever, you probably want to draw it on a MapView. But how to do it? That’s what I will show you now.
Create a suiting Overlay
We basically need a Overlay that takes two Geopoints and maybe a color in which the lines should be drawn. So here We have:
public class RouteOverlay extends Overlay { private GeoPoint gp1; private GeoPoint gp2; private int color; public RouteOverlay(GeoPoint gp1, GeoPoint gp2, int color) { this.gp1 = gp1; this.gp2 = gp2; this.color = color; }
Now all that’s left now for our Overlay is to override the draw() method and draw the line as we need it:
@Override public void draw(Canvas canvas, MapView mapView, boolean shadow) { Projection projection = mapView.getProjection(); Paint paint = new Paint(); Point point = new Point(); projection.toPixels(gp1, point); paint.setColor(color); Point point2 = new Point(); projection.toPixels(gp2, point2); paint.setStrokeWidth(5); paint.setAlpha(120); canvas.drawLine(point.x, point.y, point2.x, point2.y, paint); super.draw(canvas, mapView, shadow); }
Back in the Activity, just iterate over the GeoPoints that you got from google maps and add each of them to the MapView:
private void drawPath(List geoPoints, int color) { List overlays = mapView.getOverlays(); for (int i = 1; i < geoPoints.size(); i++) { overlays.add(new RouteOverlay(geoPoints.get(i - 1), geoPoints.get(i), color)); } }
Get location updates from the location manager
So now we have the geopoints and also the overlays, but we’ve only got the last known location of the user! The app doesn’t even updates his location!
What we need to achieve this is a listener from the location manager. That’s quite simple and we also got the LocationManager ready in onCreate, so we just have to add this little line to it:
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 300000, 5000, this);
The first number here is the timespan in milliseconds in which we want want to receive the updates, the second one is the distance in meters that the user has to move before we get them. In our app we don’t have to update the route all the time, so we go with 5 minutes and 5 kilometers.
Be very careful with the values here, because the whole gps thing consumes a lot of energy! (And if the values are way to small it also blocks the whole app)
Also don’t forget to remove the listener if the MapView isn’t visible:
@Override protected void onPause() { //remove the listener locationManager.removeUpdates(this); super.onPause(); } @Override protected void onResume() { //add the listener again locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 300000, 5000, this); super.onResume(); }
Now we have to let our MapActivity implement LocationListener and react to the updates:
public class RouteActivity extends MapActivity implements LocationListener { @Override public void onLocationChanged(Location location) { drawUserPosition(location); } private void drawUserPosition(Location location) { GeoPoint currentLocation; currentLocation = new GeoPoint((int) ( location.getLatitude() * 1E6), (int) ( location getLongitude() * 1E6)); OverlayItem currentLocationOverlay = new OverlayItem(currentLocation, getString(R.string.your_location), getString(R.string.current_location)); mapOverlays.clear(); if (locationOverlays.size() > 1) { // remove the old user position if there is one locationOverlays.removeOverlay(1); } //add new user position locationOverlays.addOverlay(currentLocationOverlay, this.getResources().getDrawable(R.drawable.someImage)); mapOverlays.add(locationOverlays); //. //. calculate / set the mapcenter, zoom to span //. see in previous posts //. RouteThread rt = new RouteThread(currentLocation, synyxGeoPoint, routeHandler); rt.start(); }
Make it threaded
But we’re not quite finished yet, because you can’t just put the internet connection and parsing into the main thread! This would block the ui for quite a long time if the users internet connection isn’t so fast.
So what we have to do is to create a handler as an inner class of our Activity that takes messages from the thread which gets us the geopoints:
private class RouteHandler extends Handler { public void handleMessage(Message msg) { boolean error = msg.getData().getBoolean("error", false); if (!error) { // set the geopoints (we can't just add the overlays // to the map here, because it's on a different thread geoPoints = (List<GeoPoint>) msg.obj; post(updateRoute); } else { // maybe you want to show an error message here to // notice the user that the route can not be displayed // because there's no connection to the internet } } }
Send the geopoints from the RouteThread:
Message msg = new Message(); msg.obj = decodePoly(encoded); handler.dispatchMessage(msg);
(If you have further data to send, use a Bundle object and add it to the Message.)
And now we need the main thread to update the overlays if there are new geopoints available. Again, we need the handler to accomplish that, because it can start runnables into the same thread the handler was created in.
First we create a runnable in the Activity:
final Runnable updateRoute = new Runnable() { public void run() { // draw the path and then invalidate the mapview so that it redraws itself drawPath(geoPoints, Color.GREEN); mapView.invalidate(); } };
And all that is left to do is to call it from inside the handler:
post(updateRoute);
Anyway, this is how we solved the routing in our app. If you have a question, or have suggestions how we could make it better, please leave us a comment!
dabi
May I contact with you privately I want you to examine my piece of code please
mohamed
How can we show shortest path between 12 locations using google map version 2 in android?
rajendran
Please send the full source , really its very good..
Shinta
can you display your application running ?..i just wanna see how it work..
Thank you for the explanation..
Supakorn Masiri
Would you please to have any idea about draw a path from location data and then can edit and delete path that draw in later.
subhalaxmi
Hi Mr Knell, i want to trace the user path and want to display the path in my project. Can you give me some suggestion ho to do the same in map v2 android
Randy
Very helpful tutorial. Can you tell me what locationOverlays is? I can't figure out how to use it. Thanks!
Tobias Knell
@Randy thanks! The locationOverlays is just a simple member of the Activity to store all location marker overlays that are displayed on the map. We stored the end position as the first element in this List and the current user position as the second element.
kimwoobill
Hi TobI, i am having trouble with my project. I get an Array of geopoints from our webservice and what I need is drawing a route from these geopoints. Can you give me some sugguestions for my problem
Tobias Knell
Well, where's the problem? This is exactly what this blogpost describes!
Rein
What should I do to confirm that the code is called?
Tobias Knell
Just put a breakpoint in your code and start the app in dubugging mode from your IDE.
Rein
Hi! I was trying to draw a route between two points using json. I used this link, http://stackoverflow.com/questions/14702621/answer-draw-path-between-two-points-using-google-maps-android-api-v2 but unfortunately, when I applied it on my app, it does not draw anything.
Tobias Knell
Have you debugged into it, to confirm that the code is called? If it is, did you check that the view redraws itself after you add the line(s)?
Cássio
@Peter Nagy, try to right click the html page and show the source code. Probably, you already have the XML but not well rendered.
Peter Nagy
Hi,
It seems Google change something, because output=kml will not give back anything except html. Do you know something how could we solve this ? Is there any way to get KML with logged in user ?
Eduardo
Me again. So, I ended up using AsyncTask on for the "threading" aspect, and, at least on my case study avoiding create one overlay per each pair of points was better. Ploting all the lines on the same overlay consumes less memory than creating one overlay for each par of vertices, and consequently, for each piece of the route.
Tobias Knell
Yup, AsyncTasks are the better way for threading. And the less overlays there are, the better, I think (I don't know for really large ones, though).
Eduardo
@Tobias, thanks for you answer. My app is focused on walking mode, and hopefully I will not be drawing that many points, as it would be with driving. I am still working on that, but since I am starting on Android/Mobile, many are the caveats that differ it from the Desktop/Web world. If I have any outcome that can aggregate on your post, I will drop a line here on the comments.
Thanks again.
Eduardo
Me again, if possible, could you elaborate more on the RouteThread class? I could not get it right from your example. Thanks.
Eduardo
Really interesting approach. Instead of using kml, I got the results using JSON and therefore I receive pairs of points in plain text. Then, instead of adding one Overlay per each pair of points, I've passed the set of points to the Overlay class and iterated on it on the Draw method. Do you know how this is comparable with your approach regarding efficiency? Adding one overlay for every pair of points vs. Passing the whole set of points to the Overlay class and iterating over it there.
Thanks.
Tobias Knell
@Eduardo If you draw every single Point, the performance isn't that good. I think the Map View will start lagging very fast if you put enough points on it (It has to iterate every single Overlay on each draw I suppose). Though you may save some time and bandwidth by retrieving the JSON instead of the KML.
edit: In the RouteThread we call the methods from the previous post: Get the KML file and parse it. Then we use a Handler to send the result to the UI thread and add the new overlays there and zoom the map to the right place.
jjj
Post source code, immediately!!!
Tobias Knell
@jjj No. I want you to learn something and not just copypaste our code :)
Tobias Knell
@Mir Nauman Tahir: Thanks for sharing!
@Zhassan try to read the blogposts from Mir Nauman Tahir, he explains how this all works in smaller steps.
Zhassan
I could not understand((
Mir Nauman Tahir
its a nice tutorial but is way too complicated. i have tried to make it simple http://mirnauman.wordpress.com/2012/04/26/android-google-maps-tutorial-part-7-drawing-a-path-or-line-between-two-locations/
duongnguyen
Thanks very much. I just do it
Manoj
This is exactly what i needed. Thanks many times. I'm doing a project using OSM, hope it will not bring much conflicts when it comes to methods and parameters.
I managed the first part and anxiously waiting to implement this feature. Thx agian.
Martin
OK I have just found what I was doing wrong:
I was initially parsing my KML for all the "Point" nodes and getting the associated coordinates.
Instead, we must only get the "coordinates" node of the "Placemark" which has the "name" = "Route".
This gives us all the detailed coordinates to draw the route and it works absolutely perfectly.
Thanks again !
Regards
Martin
Sorry, the previous comment was obviously parsed and the node names were taken out.
What I meant to say was:
The fact that we only use the "coordinates" nodes, and don’t take into account the different "LookAt" nodes and its sub-nodes means that some essential parts of the route are skipped.
Martin
Thank you for this set of articles which has pointed me in the right direction.
I've succesfully implemented what you suggested.
However, the route drawn doesn't actually really correspond to the same route in google maps.
The fact that we only use the nodes, and don't take into account the different nodes and its sub-nodes means that some essential parts of the route are skipped.
Curved roads for example are ignored as we just draw a straight line from one point to the next.
Have you found a way to overcome this issue ?
Regards,
LAT
Can you tell me limit the google maps for a country like Uk? And we won't see another countries when the google maps is loaded
Tobias Knell
Sorry, I haven't worked with google maps for a while and also haven't tried limiting the map. Try searching for something like "android google maps bounding box".
Anirudh
i implemented your code but, for some reason line is not drawn on map...
i have used itemizedoverlay..
when i changed it to overlay my icon on mao is also not displayed..
plz en light me...
Maik
How would you print the driving directions?
Tobias Knell
On Phones I would probably use the menu to let the user open another view (on top of the mapview) that contains the driving directions.
On Tablets I'd show them in a ScrollView on the left or right side of the map.
venus
This is nice tutorial Please put the full source code if you can that is very help full for all
Tobias Knell
Sorry, but that's nearly all that you need to get it running. This tutorial wasn't intended to deliver a fully working routing App, but rather to give you some hints on how you could do it / to show how we did it for a simple purpose.
usman
This post needs a dire pressence of a link that says download full source code
Antonio de Sousa
Hi, thanks for these couple of articles. Please,if you make the source code downloable will be really cool.
تونسي و راسي عالي
Great Tutorial, Please put the full source code if you can, that will be really helpful, thanks again
Thomas
Yes it will be greatful if you could make downloadable the code. Again thanks for Ur tuto, it's great.
okan
Can you please put your code of the tutorial here, so we can download it.
levisl
Yes, this article was extremely helpful, and very easy to follow. Thank you!
Cdsap
Hi, great couple of articles!!!