domingo, 23 de octubre de 2016

rutas y direcciones google maps api



Google Maps Draw Route between two points using Google Directions in Google Map Android API V2



Android Tutorial Point Download Now

In this post we will develop an Android App to draw route on google maps (namely Google Maps Draw Route). Here we will first add our location on google map and then allow user to select two points. A marker will be added to those two points and a route will be drawn. Note: We have made a similar tutorial of draw route along with showing distance and duration between origin and destination in a much easier way. For that you can refer our tutorial of Google Maps Distance Calculator using Google Directions in Google Maps Android API.




Pre-requisites:

1) Android Studio installed on your PC (Unix or Windows). You can learn how to install it here .
2) A real time android device (Smartphone or Tablet) configured with Android Studio. .
3) A basic knowledge of Android lifecycle and different classes & functions used in Android Studio.
Before going through this post we will suggest you to first have a look at our post on How to get current location in Android Google Map. That information (inside link) will be used in this post.
Now let’s make it. We hope you would have already made an App to display current user location. So we are not repeating that part now.

Creating new project

Please follow following steps:
  1. Open Android Studio and make a new project with name “Google Maps Draw Route” and company domain application.example.com (I used my company domain i.e androidtutorialpoint.com. Similarly you can use yours).
  2. Click Next and choose android version Lollipop. Again Click Next and Choose Google Maps Activity (as shown in following pic).
  3. Google_Map_Activity
  4. Leave all things remaining same and Click Finish.
Now you will be able to see three files:
  1. google_maps_api.xml (…/GoogleMapsDrawRoute/app/src/debug/res/values/google_maps_api.xml)
  2. MapsActivity.java (…/GoogleMapsDrawRoute/app/src/main/java/com/androidtutorialpoint/googlemapsdrawroute/MapsActivity.java)
  3. AndroidManifest.xml ( …/GoogleMapsDrawRoute/app/src/main/AndroidManifest.xml)

Configuring Google Play Services

Open google_maps_api.xml. Here you will find a lot of information along with a link. Copy-Paste this link in your web browser. Make a Gmail account also through which you will configure google play services.
MapApp_key_1
Now at the browser choose “Create New Project” and Click Continue. Following screen will be displayed:
MapApp_key_2
Click on Go to credentials. Below screen will appear.
MapApp_key_3
Create your key by clicking Create. Now a key will be created that you shall copy and paste in google_maps_api.xml. Copy paste it in place where YOUR_KEY_HERE is written:
Code inside google_maps_api.xml is complete.

google_maps_api.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<resources>
    <!--
    TODO: Before you run your application, you need a Google Maps API key.
    To get one, follow this link, follow the directions and press "Create" at the end:
    You can also add your credentials to an existing key, using this line:
    xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx;com.androidtutorialpoint.googlemapsapp
    Alternatively, follow the directions here:
    Once you have your key (it starts with "AIza"), replace the "google_maps_key"
    string in this file.
    -->
    <string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">LVwrKoLOEMgwUBXGiut0bkFhoAjOiaVemoMlymg</string>    
</resources>
Also one more thing to do here is to enable corresponding directions API. In the following image click Google Maps Directions API and then enable it:
Google Maps Distance Calculator

Code Inside AndroidManifest.xml:

If you go inside AndroidManifest.xml then this key will be displayed in meta tags. Here you need to add permissions for accessing location of device. The required permission should be as follows:
ACCESS_NETWORK_STATE – To check network state i.e if we are connected to any network or not.
INTERNET – If we are connected to Internet or not.
ACCESS_COARSE_LOCATION – To determine user’s location using WiFi and mobile. It will give us an approximate location.
ACCESS_FINE_LOCATION – To determine user’s location using GPS. It will give us precise location.
OpenGL ES V2 – Required for Google Maps V2

AndroidManifest.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.androidtutorialpoint.googlemapsdrawroute"
          xmlns:android="http://schemas.android.com/apk/res/android">
    <!--
         The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
         Google Maps Android API v2, but you must specify either coarse or fine
         location permissions for the 'MyLocation' functionality.
    -->
    <uses-permission android:name="com.androidtutorialpoint.mymapsappsdirection.permission.MAPS_RECEIVE" />
    <uses-permission android:name="android.permission.INTERNET"/>
    <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"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <!--
             The API key for Google Maps-based APIs is defined as a string resource.
             (See the file "res/values/google_maps_api.xml").
             Note that the API key is linked to the encryption key used to sign the APK.
             You need a different API key for each encryption key, including the release key that is used to
             sign the APK for publishing.
             You can define the keys for the debug and release targets in src/debug/ and src/release/.
        -->
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="@string/google_maps_key"/>
        <activity
            android:name=".MapsActivity"
            android:label="@string/title_activity_maps">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
</manifest>

Code inside MapsActivity.java:

This is heart of our code. We will divide this code into parts and debug it one by one. Here we won’t discuss code related to getting current user location. You can get that information here.
1) Setting OnClickListener:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// Setting onclick event listener for the map
      mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
          @Override
          public void onMapClick(LatLng point) {
              // Already two locations
              if (MarkerPoints.size() > 1) {
                  MarkerPoints.clear();
                  mMap.clear();
              }
              // Adding new item to the ArrayList
              MarkerPoints.add(point);
              // Creating MarkerOptions
              MarkerOptions options = new MarkerOptions();
              // Setting the position of the marker
              options.position(point);
              /**
               * For the start location, the color of marker is GREEN and
               * for the end location, the color of marker is RED.
               */
              if (MarkerPoints.size() == 1) {
                  options.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN));
              } else if (MarkerPoints.size() == 2) {
                  options.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED));
              }
              // Add new marker to the Google Map Android API V2
              mMap.addMarker(options);
              // Checks, whether start and end locations are captured
              if (MarkerPoints.size() >= 2) {
                  LatLng origin = MarkerPoints.get(0);
                  LatLng dest = MarkerPoints.get(1);
                  // Getting URL to the Google Directions API
                  String url = getUrl(origin, dest);
                  Log.d("onMapClick", url.toString());
                  FetchUrl FetchUrl = new FetchUrl();
                  // Start downloading json data from Google Directions API
                  FetchUrl.execute(url);
                  //move map camera
                  mMap.moveCamera(CameraUpdateFactory.newLatLng(origin));
                  mMap.animateCamera(CameraUpdateFactory.zoomTo(11));
              }
          }
      });
Above code will be executed as soon as user tap on Android screen. It will be used to place marker at the points between which path will be drawn. Coordinates of tapped points will be accessed and stored using MarkerPoints.get() and LatLng origin respectively.Url will be fetched and implemented using Async Task FetchUrl.
2) Async Task:
AsyncTask is an abstract class provided by Android which helps us to use the UI thread properly. This class allows us to perform long/background operations and show its result on the UI thread without having to manipulate threads. You can get more information about it here. Here doInBackground task will be implemented in background and onPostExecute will be shown on GUI.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Fetches data from url passed
  private class FetchUrl extends AsyncTask<String, Void, String> {
      @Override
      protected String doInBackground(String... url) {
          // For storing data from web service
          String data = "";
          try {
              // Fetching the data from web service
              data = downloadUrl(url[0]);
              Log.d("Background Task data", data.toString());
          } catch (Exception e) {
              Log.d("Background Task", e.toString());
          }
          return data;
      }
      @Override
      protected void onPostExecute(String result) {
          super.onPostExecute(result);
          ParserTask parserTask = new ParserTask();
          // Invokes the thread for parsing the JSON data
          parserTask.execute(result);
      }
  }
downloadUrl is used to fecth url from web service and result is parsed using ParserTask (Async Task).
3) Code inside downloadUrl:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
private String downloadUrl(String strUrl) throws IOException {
      String data = "";
      InputStream iStream = null;
      HttpURLConnection urlConnection = null;
      try {
          URL url = new URL(strUrl);
          // Creating an http connection to communicate with url
          urlConnection = (HttpURLConnection) url.openConnection();
          // Connecting to url
          urlConnection.connect();
          // Reading data from url
          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();
          Log.d("downloadUrl", data.toString());
          br.close();
      } catch (Exception e) {
          Log.d("Exception", e.toString());
      } finally {
          iStream.close();
          urlConnection.disconnect();
      }
      return data;
  }
Data returned from web will be in json format which user can get using HttpURLConnection. You can get more information about how to get data over web here. So this task will return json data returned from web.
4) Code inside Parser Task:
Define a new class with the name ParserTask which will parse the Json data retuned by downloadUrl.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
private class ParserTask extends AsyncTask<String, Integer, List<List<HashMap<String, String>>>> {
        // Parsing the data in non-ui thread
        @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]);
                Log.d("ParserTask",jsonData[0].toString());
                DataParser parser = new DataParser();
                Log.d("ParserTask", parser.toString());
                // Starts parsing data
                routes = parser.parse(jObject);
                Log.d("ParserTask","Executing routes");
                Log.d("ParserTask",routes.toString());
            } catch (Exception e) {
                Log.d("ParserTask",e.toString());
                e.printStackTrace();
            }
            return routes;
        }
        // Executes in UI thread, after the parsing process
        @Override
        protected void onPostExecute(List<List<HashMap<String, String>>> result) {
            ArrayList<LatLng> points;
            PolylineOptions lineOptions = null;
            // Traversing through all the routes
            for (int i = 0; i < result.size(); i++) {
                points = new ArrayList<>();
                lineOptions = new PolylineOptions();
                // Fetching i-th route
                List<HashMap<String, String>> path = result.get(i);
                // Fetching all the points in i-th route
                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);
                }
                // Adding all the points in the route to LineOptions
                lineOptions.addAll(points);
                lineOptions.width(10);
                lineOptions.color(Color.RED);
                Log.d("onPostExecute","onPostExecute lineoptions decoded");
            }
            // Drawing polyline in the Google Map for the i-th route
            if(lineOptions != null) {
                mMap.addPolyline(lineOptions);
            }
            else {
                Log.d("onPostExecute","without Polylines drawn");
            }
        }
    }
In the above code doInBackground will actually parse the data and onPostExecute will draw route on Google Maps.
5) Parsing data:
Make a separate java class with the name DataParser at the path: GoogleMapsDrawRoute/app/src/main/java/com/androidtutorialpoint/googlemapsdrawroute/

DataParser class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
public class DataParser {
    /** Receives a JSONObject and returns a list of lists containing latitude and longitude */
    public List<List<HashMap<String,String>>> parse(JSONObject jObject){
        List<List<HashMap<String, String>>> routes = new ArrayList<>() ;
        JSONArray jRoutes;
        JSONArray jLegs;
        JSONArray jSteps;
        try {
            jRoutes = jObject.getJSONArray("routes");
            /** Traversing all routes */
            for(int i=0;i<jRoutes.length();i++){
                jLegs = ( (JSONObject)jRoutes.get(i)).getJSONArray("legs");
                List path = new ArrayList<>();
                /** 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<>();
                            hm.put("lat", Double.toString((list.get(l)).latitude) );
                            hm.put("lng", Double.toString((list.get(l)).longitude) );
                            path.add(hm);
                        }
                    }
                    routes.add(path);
                }
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }catch (Exception e){
        }
        return routes;
    }
    /**
     * Method to decode polyline points
     * */
    private List<LatLng> decodePoly(String encoded) {
        List<LatLng> poly = new ArrayList<>();
        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;
    }
}
  
In the above code, I have added comments wherever required. Still if you have doubt then please ask in comments.
6) Draw route on google maps:
Route is drawn using Google Maps polyline. Add following code in onPostExecute :

onPostExecute

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Override
protected void onPostExecute(List<List<HashMap<String, String>>> result) {
    ArrayList<LatLng> points;
    PolylineOptions lineOptions = null;
    // Traversing through all the routes
    for (int i = 0; i < result.size(); i++) {
        points = new ArrayList<>();
        lineOptions = new PolylineOptions();
        // Fetching i-th route
        List<HashMap<String, String>> path = result.get(i);
        // Fetching all the points in i-th route
        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);
        }
        // Adding all the points in the route to LineOptions
        lineOptions.addAll(points);
        lineOptions.width(10);
        lineOptions.color(Color.RED);
        Log.d("onPostExecute","onPostExecute lineoptions decoded");
    }
    // Drawing polyline in the Google Map for the i-th route
    if(lineOptions != null) {
        mMap.addPolyline(lineOptions);
    }
    else {
        Log.d("onPostExecute","without Polylines drawn");
    }
Finally in the above code points are fetched from result and drawn on Google Map.ArrayList points is used to store position on Google Map. Route is drawn using google maps polyline lineOptions.addAll(points).
You can see full code in following files:
MapsActivity.java
DataParser.java
google_maps_api.xml
AndroidManifest.xml
So finally our app is complete. We would suggest you to turn on GPS and Internet Connection. Run this route maker on any real android device. It will first display your location. Now tap on any two locations and it will show route between them.




What’s Next

You can experiment with different locations around world and even make a list of bars or restaurants near you and draw path between them. Moreover you can learn How to search nearby locations and add marker on places like Restaurants, Schools, Hospitals etc. in Google Maps.
Thanks for reading guys. If you have any doubt or suggestions then please comment. Don’t forget to subscribe our blog for latest android tutorials. Also do Like our Facebook Page or Add us on Twitter. Happy Coding 🙂
You can download full code below

Android Tutorial Point Download Now