Draw Path on Google Maps Android API

This Android tutorial is to demonstrate a sample application which will draw path for a route in Google map using Android API v2. This tutorial is a part of Google maps series. I recommend you to go through the previous tutorial Draw lines on Google Maps Android API. This earlier tutorial is to draw straight lines (polyline) between given latitude and longitude.

The example Android app given in this tutorial give opportunity to learn to add markers, zoom to a particular latitude/longitude, draw polylines and draw path along a route in Google map. Pre-requisite is same for all Google maps tutorial and so I am not going to repeat it again, so to setup the development environment, to get the Android API key and related preparations please go through the previous tutorial link given above.

PathGoogleMap

Above shown image is the output of the example Android app. I have chose three locations and drawn path between them. Those three locations are shown by Android markers.

The Google Directions API

To get the directions to draw the path, I have used the Google Directions API. We just hit the given URL using HTTP request and get json response. This Google service calculates direction between given locations.

PathGoogleMapActivity.java

package com.javapapers.android.maps.path;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.json.JSONObject;

import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.Log;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolylineOptions;

public class PathGoogleMapActivity extends FragmentActivity {

	private static final LatLng LOWER_MANHATTAN = new LatLng(40.722543,
			-73.998585);
	private static final LatLng BROOKLYN_BRIDGE = new LatLng(40.7057, -73.9964);
	private static final LatLng WALL_STREET = new LatLng(40.7064, -74.0094);

	GoogleMap googleMap;
	final String TAG = "PathGoogleMapActivity";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_path_google_map);
		SupportMapFragment fm = (SupportMapFragment) getSupportFragmentManager()
				.findFragmentById(R.id.map);
		googleMap = fm.getMap();

		MarkerOptions options = new MarkerOptions();
		options.position(LOWER_MANHATTAN);
		options.position(BROOKLYN_BRIDGE);
		options.position(WALL_STREET);
		googleMap.addMarker(options);
		String url = getMapsApiDirectionsUrl();
		ReadTask downloadTask = new ReadTask();
		downloadTask.execute(url);

		googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(BROOKLYN_BRIDGE,
				13));
		addMarkers();

	}

	private String getMapsApiDirectionsUrl() {
		String waypoints = "waypoints=optimize:true|"
				+ LOWER_MANHATTAN.latitude + "," + LOWER_MANHATTAN.longitude
				+ "|" + "|" + BROOKLYN_BRIDGE.latitude + ","
				+ BROOKLYN_BRIDGE.longitude + "|" + WALL_STREET.latitude + ","
				+ WALL_STREET.longitude;

		String sensor = "sensor=false";
		String params = waypoints + "&" + sensor;
		String output = "json";
		String url = "https://maps.googleapis.com/maps/api/directions/"
				+ output + "?" + params;
		return url;
	}

	private void addMarkers() {
		if (googleMap != null) {
			googleMap.addMarker(new MarkerOptions().position(BROOKLYN_BRIDGE)
					.title("First Point"));
			googleMap.addMarker(new MarkerOptions().position(LOWER_MANHATTAN)
					.title("Second Point"));
			googleMap.addMarker(new MarkerOptions().position(WALL_STREET)
					.title("Third Point"));
		}
	}

	private class ReadTask extends AsyncTask<String, Void, String> {
		@Override
		protected String doInBackground(String... url) {
			String data = "";
			try {
				HttpConnection http = new HttpConnection();
				data = http.readUrl(url[0]);
			} catch (Exception e) {
				Log.d("Background Task", e.toString());
			}
			return data;
		}

		@Override
		protected void onPostExecute(String result) {
			super.onPostExecute(result);
			new ParserTask().execute(result);
		}
	}

	private class ParserTask extends
			AsyncTask<String, Integer, List<List<HashMap<String, String>>>> {

		@Override
		protected List<List<HashMap<String, String>>> doInBackground(
				String... jsonData) {

			JSONObject jObject;
			List<List<HashMap<String, String>>> routes = null;

			try {
				jObject = new JSONObject(jsonData[0]);
				PathJSONParser parser = new PathJSONParser();
				routes = parser.parse(jObject);
			} catch (Exception e) {
				e.printStackTrace();
			}
			return routes;
		}

		@Override
		protected void onPostExecute(List<List<HashMap<String, String>>> routes) {
			ArrayList<LatLng> points = null;
			PolylineOptions polyLineOptions = null;

			// traversing through routes
			for (int i = 0; i < routes.size(); i++) {
				points = new ArrayList<LatLng>();
				polyLineOptions = new PolylineOptions();
				List<HashMap<String, String>> path = routes.get(i);

				for (int j = 0; j < path.size(); j++) {
					HashMap<String, String> point = path.get(j);

					double lat = Double.parseDouble(point.get("lat"));
					double lng = Double.parseDouble(point.get("lng"));
					LatLng position = new LatLng(lat, lng);

					points.add(position);
				}

				polyLineOptions.addAll(points);
				polyLineOptions.width(2);
				polyLineOptions.color(Color.BLUE);
			}

			googleMap.addPolyline(polyLineOptions);
		}
	}
}

HttpConnection.java

package com.javapapers.android.maps.path;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

import android.util.Log;

public class HttpConnection {
	public String readUrl(String mapsApiDirectionsUrl) throws IOException {
		String data = "";
		InputStream iStream = null;
		HttpURLConnection urlConnection = null;
		try {
			URL url = new URL(mapsApiDirectionsUrl);
			urlConnection = (HttpURLConnection) url.openConnection();
			urlConnection.connect();
			iStream = urlConnection.getInputStream();
			BufferedReader br = new BufferedReader(new InputStreamReader(
					iStream));
			StringBuffer sb = new StringBuffer();
			String line = "";
			while ((line = br.readLine()) != null) {
				sb.append(line);
			}
			data = sb.toString();
			br.close();
		} catch (Exception e) {
			Log.d("Exception while reading url", e.toString());
		} finally {
			iStream.close();
			urlConnection.disconnect();
		}
		return data;
	}

}

PathJSONParser.java

package com.javapapers.android.maps.path;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.google.android.gms.maps.model.LatLng;

public class PathJSONParser {

	public List<List<HashMap<String, String>>> parse(JSONObject jObject) {
		List<List<HashMap<String, String>>> routes = new ArrayList<List<HashMap<String, String>>>();
		JSONArray jRoutes = null;
		JSONArray jLegs = null;
		JSONArray jSteps = null;
		try {
			jRoutes = jObject.getJSONArray("routes");
			/** Traversing all routes */
			for (int i = 0; i < jRoutes.length(); i++) {
				jLegs = ((JSONObject) jRoutes.get(i)).getJSONArray("legs");
				List<HashMap<String, String>> path = new ArrayList<HashMap<String, String>>();

				/** Traversing all legs */
				for (int j = 0; j < jLegs.length(); j++) {
					jSteps = ((JSONObject) jLegs.get(j)).getJSONArray("steps");

					/** Traversing all steps */
					for (int k = 0; k < jSteps.length(); k++) {
						String polyline = "";
						polyline = (String) ((JSONObject) ((JSONObject) jSteps
								.get(k)).get("polyline")).get("points");
						List<LatLng> list = decodePoly(polyline);

						/** Traversing all points */
						for (int l = 0; l < list.size(); l++) {
							HashMap<String, String> hm = new HashMap<String, String>();
							hm.put("lat",
									Double.toString(((LatLng) list.get(l)).latitude));
							hm.put("lng",
									Double.toString(((LatLng) list.get(l)).longitude));
							path.add(hm);
						}
					}
					routes.add(path);
				}
			}

		} catch (JSONException e) {
			e.printStackTrace();
		} catch (Exception e) {
		}
		return routes;
	}

	/**
	 * Method Courtesy :
	 * jeffreysambells.com/2010/05/27
	 * /decoding-polylines-from-google-maps-direction-api-with-java
	 * */
	private List<LatLng> decodePoly(String encoded) {

		List<LatLng> poly = new ArrayList<LatLng>();
		int index = 0, len = encoded.length();
		int lat = 0, lng = 0;

		while (index < len) {
			int b, shift = 0, result = 0;
			do {
				b = encoded.charAt(index++) - 63;
				result |= (b & 0x1f) << shift;
				shift += 5;
			} while (b >= 0x20);
			int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
			lat += dlat;

			shift = 0;
			result = 0;
			do {
				b = encoded.charAt(index++) - 63;
				result |= (b & 0x1f) << shift;
				shift += 5;
			} while (b >= 0x20);
			int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
			lng += dlng;

			LatLng p = new LatLng((((double) lat / 1E5)),
					(((double) lng / 1E5)));
			poly.add(p);
		}
		return poly;
	}
}

activity_path_google_map.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".TaskRoadMap" >

   <fragment
        android:id="@+id/map"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        class="com.google.android.gms.maps.SupportMapFragment"/>

</RelativeLayout>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.javapapers.android.maps.path"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="18" />

    <permission
        android:name="com.javapapers.android.maps.path.permission.MAPS_RECEIVE"
        android:protectionLevel="signature" />

    <uses-permission android:name="com.javapapers.android.maps.path.permission.MAPS_RECEIVE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.javapapers.android.maps.path.PathGoogleMapActivity"
            android:label="@string/title_activity_path_google_map" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <meta-data
            android:name="com.google.android.maps.v2.API_KEY"
            android:value="AIzaSyARCTcX8bABCDE_ohscNcALAak-HnjTO5s" />
        
        <meta-data 
            android:name="com.google.android.gms.version" 
            android:value="@integer/google_play_services_version" />

    </application>

</manifest>

Download Example Android App:Path Google Maps Api v2

This Android tutorial was added on 14/03/2014.

Comments on "Draw Path on Google Maps Android API" Tutorial:

  1. shaas says:

    how could i pass the latlng points of route to php using json….help plz

  2. malzoubi says:

    thank alot for your code , but can help me to give direction (nevigator)for moving object

  3. Dhananjay says:

    Very helpful tutorial.Thankssss

  4. lll says:

    is this code work for linking more than 3 point ?

  5. Maurizio says:

    Nice piece of code!

  6. andriy says:

    Thanks for this tutorial

  7. Gaurav Kumar Garg says:

    Perfect running this code.

    Thnxxxx

  8. raghu says:

    this code crashes when used continuosly
    -it gives File not found exception

    Another exception iwas facing was the “SSL handshake exception”

    Anyone has solution for these?

  9. Anonymous says:

    Hi Joe!

    Thank you for the wonderful tutorial.

    But why are there “>>” all over the place? I’ve never seen them before except when right shift operators are used. Can you please explain why?

  10. Joe says:

    Oops! thanks for pointing out. That’s a mistake. The code highlighter has swallowed some part and showing the remaining. I will fix them now.

  11. Saurabh says:

    I’m so much thankful to you :D
    Tried everything, every possible code off the net all in vain except your code.
    Very easy working implementation. Frankly did not understand anything of the code. Possibly you could give an explanation for how the code works!

  12. Swagata Banerjee says:

    does this code run on android emulator? or only real hand set?

  13. Swagata Banerjee says:

    It seems the code has successfully run for you. Did you run it on handset or emulator?

  14. John says:

    What if i want to add marker dynamically based on latitude and longitude? suppose i have a button to add markers

  15. David says:

    Why does it say that:

    —ln 36

    “Multiple markers at this line
    – activity_path_google_map cannot be resolved or is
    not a field
    – Occurrence of ‘setContentView'”

    and

    —ln 38

    “map cannot be resolved or is not a field”

    in PathGoogleMapActivity.java, when I try to import it.

    It seems like it’s missing the R file.

  16. ajay says:

    Hi.

    It showing empty map., pls help me

  17. Amol Kisan Tholbare says:

    How do i draw path between two city which is enter by user in two edittext and distance between them?

    please sir help me

  18. Amol Kisan Tholbare says:

    Download the google api in your sdk thats it

  19. Rajiv says:

    Nice Code ..

  20. Anonymous says:

    Your tutorial helped me big time. Thanks

  21. Hemal Adani says:

    Hi Joe:

    In my eclipse. It shows error all over ‘>>’ operator. Can you help me with that?

    Regards,
    Hemal Adani

  22. Hemal Adani says:

    I think I found it. I downloaded the code instead of copying it. Let see if that works. Thanks joe.

  23. Hemal Adani says:

    Hi Joe: The errors are resolved with downloaded code. Thanks. I’ll check on device now.

  24. Hemal Adani says:

    The code worked for me. Drawed Route as per I needed. Thank you.

  25. Francisco says:

    Save my life last minute.

  26. fred says:

    Thank s a lot. It has been a long time i been trying to do something like this

  27. Anonymous says:

    What kind of google api do you mention?

  28. dods says:

    I have got this error that points to manifest.
    error: Error: No resource found that matches the given name (at ‘value’ with value ‘@integer/google_play_services_version’).

  29. Harshil says:

    Hi,
    the URL, which is being generated in the code is wrong :
    it should be like :
    “https://maps.googleapis.com/maps/api/directions/json?origin=17.449797,78.373037&destination=17.47989,78.390095&%20waypoints=optimize:true|17.449797,78.373037||17.47989,78.390095&sensor=false”

  30. Anand says:

    I agree with Harshil. URL generated in the above code miss origin and destination, which are required feilds.
    Refer this: https://developers.google.com/maps/documentation/directions/

    I was facing error:
    java.lang.NullPointerException: Attempt to invoke interface method ‘int java.util.List.size()’ on a null object reference

    as URL was not returning anything.

  31. Anand says:

    Apart from URL thing, everything else works pretty smooth.
    Nice work Joe!

  32. yogesh says:

    java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/android/gms/maps/model/LatLng

  33. nadeesha says:

    It gives me a NullPointerException in for loop
    ” for (int i = 0; i < routes.size(); i++) {"

  34. param says:

    your code is not working

  35. Pedja says:

    It seems you need to add origin and destination to url.

    Replace this:
    String params = waypoints + “&” + sensor;

    With this:
    String origin = “origin=” + LOWER_MANHATTAN.latitude + “,” + LOWER_MANHATTAN.longitude;
    String destination = “destination=” + WALL_STREET.latitude + “,” + WALL_STREET.longitude;
    String params = origin + “&” + destination + “&” + waypoints + “&” + sensor;

  36. Sunny says:

    Thanks bro.. :)

  37. Tanveer says:

    I am unable to run. It says Unable to instantiate activity…

  38. Dimitr says:

    The way it worked for me:

    Replace this:
    String params = waypoints + “&” + sensor;

    With this:
    String origin = “origin=” + LOWER_MANHATTAN.latitude + “,” + LOWER_MANHATTAN.longitude;
    String destination = “destination=” + WALL_STREET.latitude + “,” + WALL_STREET.longitude;
    String params = origin + “&” + destination + “&%20” + waypoints + “&” + sensor;

  39. Shuvo Joseph says:

    Thanks for such a nice tutorial. Just needed to change the URL.

  40. fatou says:

    error opening trace file: No such file or directory (2)

Comments are closed for this "Draw Path on Google Maps Android API" tutorial.