Ludovic ROLAND - Le blog

Blog technique d'un ingénieur informatique

Android : des itinéraires dans vos applications grâce à l’api google direction !

| Comments

Dans mon dernier article, je vous montrais comment intégrer des itinéraires dans vos applications Windows Phone 7 grâce aux services Microsoft. Cette semaine, je vous propose de faire la même chose sur la plate-forme de Google.

Ce que nous allons faire

L’application que nous allons produire sera constituée de deux écrans:

  • un écran permettant de saisir les lieux de départ et d’arrivée ;
  • un écran affichant l’itinéraire sur la carte ;

L’écran de saisie des lieux de départ de d’arrivée

activity_main.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
48
49
50
51
52
53
54
55
56
57
58
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="10dp" >

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="3"
        android:orientation="horizontal" >

        <TextView
            android:text="Départ"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:textStyle="bold" />

        <EditText
            android:id="@+id/editDepart"
            android:layout_weight="2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:maxLines="1"
            android:inputType="text"
            android:lines="1" />
     </LinearLayout>

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="3"
        android:orientation="horizontal" >

        <TextView
            android:text="Arrivée"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:textStyle="bold" />

        <EditText
            android:id="@+id/editArrivee"
            android:layout_weight="2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:maxLines="1"
            android:inputType="text"
            android:lines="1" />
    </LinearLayout>

    <Button
        android:id="@+id/btnSearch"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:text="Rechercher"/>
</LinearLayout>

Dans notre Activity, nous allons simplement vérifier que les champs ne sont pas vide afin de les passer à l’activité qui affichera la carte :

MainActivity.java
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
public class MainActivity extends Activity {
    private EditText editDepart;
    private EditText editArrivee;
    private Button btnRechercher;

    /**
    * {@inheritDoc}
    */
    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //On récupère les composants graphiques
        editDepart = (EditText) findViewById(R.id.editDepart);
        editArrivee = (EditText) findViewById(R.id.editArrivee);
        btnRechercher = (Button) findViewById(R.id.btnSearch);

        btnRechercher.setOnClickListener(new OnClickListener() {
            /**
            * {@inheritDoc}
            */
            @Override
            public void onClick(final View v) {
                if("".equals(editDepart.getText().toString().trim())) {
                    Toast.makeText(MainActivity.this, "Merci de saisir un lieu de départ", Toast.LENGTH_SHORT).show();
                }
                else if("".equals(editArrivee.getText().toString().trim())) {
                    Toast.makeText(MainActivity.this, "Merci de saisir un lieu d'arrivée", Toast.LENGTH_SHORT).show();
                }
                else {
                    //On transmet les données à l'activité suivante
                    final Intent intent = new Intent(MainActivity.this, MapActivity.class);
                    intent.putExtra("DEPART", editDepart.getText().toString().trim());
                    intent.putExtra("ARRIVEE", editArrivee.getText().toString().trim());

                    MainActivity.this.startActivity(intent);
                }              
            }
        });
    }
}

L’affichage de la carte et calcul de l’itinéraire

L’affichage de la carte

Avant de faire la recherche, je vous propose de mettre en place l’écran qui accueillera la carte sur laquelle l’itinéraire sera tracée. Il s’agit d’une simple carte Google. Pour plus d’information sur la carte et comment récupérer une clef, vous pouvez lire cet article.

Votre activité doit alors ressembler à ça :

MapActivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MapActivity extends Activity {
    private GoogleMap gMap;

    /**
    * {@inheritDoc}
    */
    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_map);

        //On récupère les composants graphiques
        gMap = ((MapFragment)getFragmentManager().findFragmentById(R.id.map)).getMap();

        //On récupère le départ et l'arrivée
        final String editDepart = getIntent().getStringExtra("DEPART");
        final String editArrivee = getIntent().getStringExtra("ARRIVEE");

        //TODO: Appeler une tâche asynchrone qui affiche l'itinéraire sur la carte
    }
}

Le calcul de l’itinéraire

Nous allons maintenant créer une tâche asynchrone qui a deux objectifs :

  • appeler l’API Google Direction pour récupérer l’itinéraire ;
  • mettre à jour la carte.

Dans cette tâche asynchrone, plusieurs attributs seront nécessaires :

  • le contexte ;
  • la carte afin de la mettre à jour ;
  • le lieu de départ ;
  • le lieu d’arrivée.

Nous allons également avoir besoin d’un tableau qui contiendra les latitudes et longitudes des différents points à afficher sur la carte.

Voici alors la structure de notre tâche asynchrone :

ItineraireTask.java
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
public class ItineraireTask extends AsyncTask<Void, Integer, Boolean> {
    private static final String TOAST_MSG = "Calcul de l'itinéraire en cours";
    private static final String TOAST_ERR_MAJ = "Impossible de trouver un itinéraire";

    private Context context;
    private GoogleMap gMap;
    private String editDepart;
    private String editArrivee;
    private final ArrayList<LatLng> lstLatLng = new ArrayList<LatLng>();

    /**
    * Constructeur.
    * @param context
    * @param gMap
    * @param editDepart
    * @param editArrivee
    */
    public ItineraireTask(final Context context, final GoogleMap gMap, final String editDepart, final String editArrivee) {
        this.context = context;
        this.gMap= gMap;
        this.editDepart = editDepart;
        this.editArrivee = editArrivee;
    }

    /**
    * {@inheritDoc}
    */
    @Override
    protected void onPreExecute() {
        Toast.makeText(context, TOAST_MSG, Toast.LENGTH_LONG).show();
    }

    /***
    * {@inheritDoc}
    */
    @Override
    protected Boolean doInBackground(Void... params) {
        // TODO Auto-generated method stub
        return null;
    }

    /**
    * {@inheritDoc}
    */
    @Override
    protected void onPostExecute(final Boolean result) {   
        // TODO Auto-generated method stub
    }
}

Nous allons maintenant implémenter la méthode doInBackground dont le but est d’appeler le service Google et d’alimenter notre tableau de latitudes et longitudes.

Voici alors ce que ça donne :

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
/**
* {@inheritDoc}
*/
@Override
protected Boolean doInBackground(Void... params) {
    try {
        //Construction de l'url à appeler           
        final StringBuilder url = new StringBuilder("http://maps.googleapis.com/maps/api/directions/xml?sensor=false&language=fr");
        url.append("&origin=");
        url.append(editDepart.replace(' ', '+'));
        url.append("&destination=");
        url.append(editArrivee.replace(' ', '+'));

        //Appel du web service
        final InputStream stream = new URL(url.toString()).openStream();

        //Traitement des données
        final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setIgnoringComments(true);

        final DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();

        final Document document = documentBuilder.parse(stream);
        document.getDocumentElement().normalize();

        //On récupère d'abord le status de la requête
        final String status = document.getElementsByTagName("status").item(0).getTextContent();
        if(!"OK".equals(status)) {
            return false;
        }

        //On récupère les steps
        final Element elementLeg = (Element) document.getElementsByTagName("leg").item(0);
        final NodeList nodeListStep = elementLeg.getElementsByTagName("step");
        final int length = nodeListStep.getLength();

        for(int i=0; i<length; i++) {      
            final Node nodeStep = nodeListStep.item(i);

            if(nodeStep.getNodeType() == Node.ELEMENT_NODE) {
                final Element elementStep = (Element) nodeStep;

                //On décode les points du XML
                decodePolylines(elementStep.getElementsByTagName("points").item(0).getTextContent());
            }
        }

        return true;            
    }
    catch(final Exception e) {
        return false;
    }
}

/**
* Méthode qui décode les points en latitude et longitudes
* @param points
*/
private void decodePolylines(final String encodedPoints) {
    int index = 0;
    int lat = 0, lng = 0;

    while (index < encodedPoints.length()) {
        int b, shift = 0, result = 0;

        do {
            b = encodedPoints.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 = encodedPoints.charAt(index++) - 63;
            result |= (b & 0x1f) << shift;
            shift += 5;
        } while (b >= 0x20);

        int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
        lng += dlng;

        lstLatLng.add(new LatLng((double)lat/1E5, (double)lng/1E5));
    }
}

Comme vous pouvez le constater, il est nécessaire de décoder les points renvoyer par Google qui sont de la forme }leiHgmjM[xBKx@. A noter que je ne suis pas l’auteur de cette fonction que j’ai trouvé sur ce site.

Maintenant que nous avons toutes les données nécessaires, nous allons pouvoir mettre à jour notre carte. C’est dans la méthode onPostExecute qui tout se passe :

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
/**
* {@inheritDoc}
*/
@Override
protected void onPostExecute(final Boolean result) {   
    if(!result) {
        Toast.makeText(context, TOAST_ERR_MAJ, Toast.LENGTH_SHORT).show();
    }
    else {
        //On déclare le polyline, c'est-à-dire le trait (ici bleu) que l'on ajoute sur la carte pour tracer l'itinéraire
        final PolylineOptions polylines = new PolylineOptions();
        polylines.color(Color.BLUE);

        //On construit le polyline
        for(final LatLng latLng : lstLatLng) {
            polylines.add(latLng);
        }

        //On déclare un marker vert que l'on placera sur le départ
        final MarkerOptions markerA = new MarkerOptions();
        markerA.position(lstLatLng.get(0));
        markerA.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN));

        //On déclare un marker rouge que l'on mettra sur l'arrivée
        final MarkerOptions markerB = new MarkerOptions();
        markerB.position(lstLatLng.get(lstLatLng.size()-1));
        markerB.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED));

        //On met à jour la carte
        gMap.moveCamera(CameraUpdateFactory.newLatLngZoom(lstLatLng.get(0), 10));
        gMap.addMarker(markerA);
        gMap.addPolyline(polylines);
        gMap.addMarker(markerB);
    }
}

L’appel de la méthode asynchrone

Il ne nous reste plus qu’à appeler notre méthode asynchrone dans MapActivity.

1
new ItineraireTask(this, gMap, editDepart, editArrivee).execute();

Le code complet

Comme d’habitude, voici le code complet :

Le fichier AndroidManifest.xml

AndroidManifest.xmltélécharger
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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="fr.rolandl.blog_itineraire"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="11"
        android:targetSdkVersion="17" />

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

    <permission
        android:name="fr.rolandl.blog_itineraire.permission.MAPS_RECEIVE"
        android:protectionLevel="signature"/>

    <uses-permission android:name="fr.rolandl.blog_itineraire.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_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <meta-data
            android:name="com.google.android.maps.v2.API_KEY"
            android:value="A..."/>

        <activity
            android:name="fr.rolandl.blog_itineraire.MainActivity"
            android:configChanges="keyboardHidden|orientation|screenSize"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <activity
            android:name="fr.rolandl.blog_itineraire.MapActivity"
            android:label="CARTE"
            android:configChanges="keyboardHidden|orientation|screenSize" />
    </application>
</manifest>

Le fichier activity_main.xml

activity_main.xmltélécharger
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
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          android:orientation="vertical"
          android:padding="10dp" >

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="fill_parent"
          android:layout_height="0dp"
          android:layout_weight="3"
          android:orientation="horizontal" >

      <TextView
      android:text="Départ"
      android:layout_width="0dp"
      android:layout_weight="1"
      android:layout_height="wrap_content"
          android:textStyle="bold" />

      <EditText
          android:id="@+id/editDepart"
            android:layout_weight="2"
          android:layout_width="0dp"
          android:layout_height="wrap_content"
          android:maxLines="1"
            android:inputType="text"
            android:lines="1" />
      </LinearLayout>

       <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="fill_parent"
          android:layout_height="0dp"
          android:layout_weight="3"
          android:orientation="horizontal" >

          <TextView
              android:text="Arrivée"
              android:layout_width="0dp"
              android:layout_weight="1"
              android:layout_height="wrap_content"
              android:textStyle="bold" />

          <EditText
              android:id="@+id/editArrivee"
              android:layout_weight="2"
              android:layout_width="0dp"
              android:layout_height="wrap_content"
              android:maxLines="1"
              android:inputType="text"
              android:lines="1" />
      </LinearLayout>

      <Button
          android:id="@+id/btnSearch"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent"
          android:text="Rechercher"/>
</LinearLayout>

Le fichier activity_map.xml

activity_map.xmltélécharger
1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/map"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    class="com.google.android.gms.maps.MapFragment" />

Le fichier MainActivity.java

MainActivity.javatélécharger
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
package fr.rolandl.blog_itineraire;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import fr.rolandl.blog_itineraire.R;

/**
 * MainActivity
 * @author Ludovic
 */
public class MainActivity extends Activity {
  private EditText editDepart;
  private EditText editArrivee;
  private Button btnRechercher;

  /**
  * {@inheritDoc}
  */
  @Override
  protected void onCreate(final Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      //On récupère les composants graphiques
      editDepart = (EditText) findViewById(R.id.editDepart);
      editArrivee = (EditText) findViewById(R.id.editArrivee);
      btnRechercher = (Button) findViewById(R.id.btnSearch);

      btnRechercher.setOnClickListener(new OnClickListener() {
          /**
          * {@inheritDoc}
          */
          @Override
          public void onClick(final View v) {
              if("".equals(editDepart.getText().toString().trim())) {
                  Toast.makeText(MainActivity.this, "Merci de saisir un lieu de départ", Toast.LENGTH_SHORT).show();
              }
              else if("".equals(editArrivee.getText().toString().trim())) {
                  Toast.makeText(MainActivity.this, "Merci de saisir un lieu d'arrivée", Toast.LENGTH_SHORT).show();
              }
              else {
                  //On transmet les données à l'activité suivante
                  final Intent intent = new Intent(MainActivity.this, MapActivity.class);
                  intent.putExtra("DEPART", editDepart.getText().toString().trim());
                  intent.putExtra("ARRIVEE", editArrivee.getText().toString().trim());

                  MainActivity.this.startActivity(intent);
              }                
          }
      });
  }
}

Le fichier MapActivity.java

MapActivity.javatélécharger
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
package fr.rolandl.blog_itineraire;

import android.app.Activity;
import android.os.Bundle;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;

/**
 * MapActivity
 * @author Ludovic
 */
public class MapActivity extends Activity {
  private GoogleMap gMap;

  /**
  * {@inheritDoc}
  */
  @Override
  protected void onCreate(final Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_map);

      //On récupère les composants graphiques
      gMap = ((MapFragment)getFragmentManager().findFragmentById(R.id.map)).getMap();

      //On récupère le départ et l'arrivée
      final String editDepart = getIntent().getStringExtra("DEPART");
      final String editArrivee = getIntent().getStringExtra("ARRIVEE");

      //Appel de la méthode asynchrone
      new ItineraireTask(this, gMap, editDepart, editArrivee).execute();
  }
}

Le fichier ItineraireTask.java

ItineraireTask.javatélécharger
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
package fr.rolandl.blog_itineraire;

import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

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

import android.content.Context;
import android.graphics.Color;
import android.os.AsyncTask;
import android.widget.Toast;

/**
 * ItineraireTask
 * @author Ludovic
 */
public class ItineraireTask extends AsyncTask<Void, Integer, Boolean> {
  /*******************************************************/
  /** CONSTANTES.
 /*******************************************************/
  private static final String TOAST_MSG = "Calcul de l'itinéraire en cours";
  private static final String TOAST_ERR_MAJ = "Impossible de trouver un itinéraire";

  /*******************************************************/
  /** ATTRIBUTS.
 /*******************************************************/
  private Context context;
  private GoogleMap gMap;
  private String editDepart;
  private String editArrivee;
  private final ArrayList<LatLng> lstLatLng = new ArrayList<LatLng>();

  /*******************************************************/
  /** METHODES / FONCTIONS.
 /*******************************************************/
  /**
  * Constructeur.
  * @param context
  * @param gMap
  * @param editDepart
  * @param editArrivee
  */
  public ItineraireTask(final Context context, final GoogleMap gMap, final String editDepart, final String editArrivee) {
      this.context = context;
      this.gMap= gMap;
      this.editDepart = editDepart;
      this.editArrivee = editArrivee;
  }

  /**
  * {@inheritDoc}
  */
  @Override
    protected void onPreExecute() {
      Toast.makeText(context, TOAST_MSG, Toast.LENGTH_LONG).show();
  }

  /***
  * {@inheritDoc}
  */
  @Override
  protected Boolean doInBackground(Void... params) {
      try {
          //Construction de l'url à appeler         
          final StringBuilder url = new StringBuilder("http://maps.googleapis.com/maps/api/directions/xml?sensor=false&language=fr");
          url.append("&origin=");
          url.append(editDepart.replace(' ', '+'));
          url.append("&destination=");
          url.append(editArrivee.replace(' ', '+'));

          //Appel du web service
          final InputStream stream = new URL(url.toString()).openStream();

          //Traitement des données
          final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
          documentBuilderFactory.setIgnoringComments(true);

          final DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();

          final Document document = documentBuilder.parse(stream);
          document.getDocumentElement().normalize();

          //On récupère d'abord le status de la requête
          final String status = document.getElementsByTagName("status").item(0).getTextContent();
          if(!"OK".equals(status)) {
              return false;
          }

          //On récupère les steps
          final Element elementLeg = (Element) document.getElementsByTagName("leg").item(0);
          final NodeList nodeListStep = elementLeg.getElementsByTagName("step");
          final int length = nodeListStep.getLength();

          for(int i=0; i<length; i++) {        
              final Node nodeStep = nodeListStep.item(i);

              if(nodeStep.getNodeType() == Node.ELEMENT_NODE) {
                  final Element elementStep = (Element) nodeStep;

                  //On décode les points du XML
                  decodePolylines(elementStep.getElementsByTagName("points").item(0).getTextContent());
              }
          }

          return true;          
      }
      catch(final Exception e) {
          return false;
      }
  }

  /**
  * Méthode qui décode les points en latitudes et longitudes
  * @param points
  */
  private void decodePolylines(final String encodedPoints) {
      int index = 0;
      int lat = 0, lng = 0;

      while (index < encodedPoints.length()) {
          int b, shift = 0, result = 0;

          do {
              b = encodedPoints.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 = encodedPoints.charAt(index++) - 63;
              result |= (b & 0x1f) << shift;
              shift += 5;
          } while (b >= 0x20);

          int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
          lng += dlng;

          lstLatLng.add(new LatLng((double)lat/1E5, (double)lng/1E5));
      }
  }

  /**
  * {@inheritDoc}
  */
  @Override
  protected void onPostExecute(final Boolean result) { 
      if(!result) {
          Toast.makeText(context, TOAST_ERR_MAJ, Toast.LENGTH_SHORT).show();
      }
      else {
          //On déclare le polyline, c'est-à-dire le trait (ici bleu) que l'on ajoute sur la carte pour tracer l'itinéraire
          final PolylineOptions polylines = new PolylineOptions();
          polylines.color(Color.BLUE);

          //On construit le polyline
          for(final LatLng latLng : lstLatLng) {
                  polylines.add(latLng);
          }

          //On déclare un marker vert que l'on placera sur le départ
          final MarkerOptions markerA = new MarkerOptions();
          markerA.position(lstLatLng.get(0));
          markerA.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN));

          //On déclare un marker rouge que l'on mettra sur l'arrivée
          final MarkerOptions markerB = new MarkerOptions();
          markerB.position(lstLatLng.get(lstLatLng.size()-1));
          markerB.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED));

          //On met à jour la carte
          gMap.moveCamera(CameraUpdateFactory.newLatLngZoom(lstLatLng.get(0), 10));
          gMap.addMarker(markerA);
          gMap.addPolyline(polylines);
          gMap.addMarker(markerB);
      }
  }
}

A lire aussi…

Comments