Licence CC BY-NC-SA

La localisation et les cartes

Ce contenu est obsolète. Il peut contenir des informations intéressantes mais soyez prudent avec celles-ci.

Nous sommes nombreux à avoir déjà utilisé Google Maps. Que ce soit pour trouver le vendeur de pizzas le plus proche, tracer l'itinéraire entre chez soi et le supermarché ou, encore mieux, regarder sa propre maison avec les images satellite.

Avec les progrès de la miniaturisation, la plupart — voire la quasi-totalité — des terminaux sont équipés de puces GPS. La géolocalisation est ainsi devenue un élément du quotidien qu'on retrouve dans énormément d'applications. On peut penser aux applications de navigation aidée par GPS, mais aussi aux applications sportives qui suivent nos efforts et élaborent des statistiques, ou encore aux applications pour noter les restaurants et les situer. On trouve ainsi deux API qui sont liées au concept de localisation :

  • Une API qui permet de localiser l'appareil.
  • Une API qui permet d'afficher des cartes.

La localisation

Préambule

On trouve tous les outils de localisation dans le package android.location.

Le GPS est la solution la plus efficace pour localiser un appareil, cependant il s'agit aussi de la plus coûteuse en batterie. Une autre solution courante est de se localiser à l'aide des points d'accès WiFi à proximité et de la distance mesurée avec les antennes relais du réseau mobile les plus proches (par triangulation).

Tout d'abord, vous devrez demander la permission dans le Manifest pour utiliser les fonctionnalités de localisation. Si vous voulez utiliser la géolocalisation (par GPS, donc), utilisez ACCESS_FINE_LOCATION ; pour une localisation plus imprécise par WiFi et antennes relais, utilisez ACCESS_COARSE_LOCATION. Enfin, si vous voulez utiliser les deux types de localisation, vous pouvez déclarer uniquement ACCESS_FINE_LOCATION, qui comprend toujours ACCESS_COARSE_LOCATION :

1
2
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

Ensuite, on va faire appel à un nouveau service système pour accéder à ces fonctionnalités : LocationManager, que l'on récupère de cette manière :

1
LocationManager locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);

Les fournisseurs de position

Vous aurez ensuite besoin d'un fournisseur de position qui sera dans la capacité de déterminer la position actuelle. On a par un exemple un fournisseur pour le GPS et un autre pour les antennes relais. Ces fournisseurs dériveront de la classe abstraite LocationProvider. Il existe plusieurs méthodes pour récupérer les fournisseurs de position disponibles sur l'appareil. Pour récupérer le nom de tous les fournisseurs, il suffit de faire List<String> getAllProviders(). Le problème de cette méthode est qu'elle va récupérer tous les fournisseurs qui existent, même si l'application n'a pas le droit de les utiliser ou qu'ils sont désactivés par l'utilisateur.

Pour ne récupérer que le nom des fournisseurs qui sont réellement utilisables, on utilisera List<String> getProviders(boolean enabledOnly). Enfin, on peut obtenir un LocationProvider à partir de son nom avec LocationProvider getProvider(String name) :

1
2
3
4
5
ArrayList<LocationProvider> providers = new ArrayList<LocationProvider>();
ArrayList<String> names = locationManager.getProviders(true);

for(String name : names)
  providers.add(locationManager.getProvider(name));

Les noms des fournisseurs sont contenus dans des constantes, comme LocationManager.GPS_PROVIDER pour le GPS et LocationManager.NETWORK_PROVIDER pour la triangulation.

Cependant, il se peut que vous ayez à sélectionner un fournisseur en fonction de critères bien précis. Pour cela, il vous faudra créer un objet de type Criteria. Par exemple, pour configurer tous les critères, on fera :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
Criteria critere = new Criteria();

// Pour indiquer la précision voulue
// On peut mettre ACCURACY_FINE pour une haute précision ou ACCURACY_COARSE pour une moins bonne précision
critere.setAccuracy(Criteria.ACCURACY_FINE);

// Est-ce que le fournisseur doit être capable de donner une altitude ?
critere.setAltitudeRequired(true);

// Est-ce que le fournisseur doit être capable de donner une direction ?
critere.setBearingRequired(true);

// Est-ce que le fournisseur peut être payant ?
critere.setCostAllowed(false);

// Pour indiquer la consommation d'énergie demandée
// Criteria.POWER_HIGH pour une haute consommation, Criteria.POWER_MEDIUM pour une consommation moyenne et Criteria.POWER_LOW pour une basse consommation
critere.setPowerRequirement(Criteria.POWER_HIGH);

// Est-ce que le fournisseur doit être capable de donner une vitesse ?
critere.setSpeedRequired(true);

Pour obtenir tous les fournisseurs qui correspondent à ces critères, on utilise List<String> getProviders(Criteria criteria, boolean enabledOnly) et, pour obtenir le fournisseur qui correspond le plus, on utilise String getBestProvider(Criteria criteria, boolean enabledOnly).

Obtenir des notifications du fournisseur

Pour obtenir la dernière position connue de l'appareil, utilisez Location getLastKnownLocation(String provider).

La dernière position connue n'est pas forcément la position actuelle de l'appareil. En effet, il faut demander à mettre à jour la position pour que celle-ci soit renouvelée dans le fournisseur. Si vous voulez faire en sorte que le fournisseur se mette à jour automatiquement à une certaine période ou tous les x mètres, on peut utiliser la méthode void requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener) avec :

  • provider le fournisseur de position.
  • minTime la période entre deux mises à jour en millisecondes. Il faut mettre une valeur supérieure à 0, sinon le fournisseur ne sera pas mis à jour périodiquement. D'ailleurs, ne mettez pas de valeur en dessous de 60 000 ms pour préserver la batterie.
  • minDistance la période entre deux mises à jour en mètres. Tout comme pour minTime, il faut mettre une valeur supérieure à 0, sinon ce critère ne sera pas pris en compte. De plus, on privilégie quand même minTime parce qu'il consomme moins de batterie.
  • listener est l'écouteur qui sera lancé dès que le fournisseur sera activé.

Ainsi, il faut que vous utilisiez l'interface LocationListener, dont la méthode void onLocationChanged(Location location) sera déclenchée à chaque mise à jour. Cette méthode de callback contient un objet de type Location duquel on peut extraire des informations sur l'emplacement donné. Par exemple, on peut récupérer la latitude avec double getLatitude() et la longitude avec double getLongitude() :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 60000, 150, new LocationListener() {

  @Override
  public void onStatusChanged(String provider, int status, Bundle extras) {

  }

  @Override
  public void onProviderEnabled(String provider) {

  }

  @Override
  public void onProviderDisabled(String provider) {

  }

  @Override
  public void onLocationChanged(Location location) {
    Log.d("GPS", "Latitude " + location.getLatitude() + " et longitude " + location.getLongitude());
  }
});

Cependant, ce code ne fonctionnera que si votre application est en cours de fonctionnement. Mais si vous souhaitez recevoir des notifications même quand l'application ne fonctionne pas ? On peut utiliser à la place void requestLocationUpdates(String provider, long minTime, float minDistance, PendingIntent intent) où le PendingIntent contenu dans intent sera lancé à chaque mise à jour du fournisseur. L'emplacement sera contenu dans un extra dont la clé est KEY_LOCATION_CHANGED.

1
2
3
4
5
Intent intent = new Intent(this, GPSUpdateReceiver.class);

PendingIntent pending = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

locationManager.requestLocationUpdates(provider, 60000, 150, pending);

On le recevra ensuite dans :

1
2
3
4
5
6
public class GPSUpdateReceiver extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {
    Location location = (Location)intent.getParcelableExtra(LocationManager.KEY_LOCATION_CHANGED);
  }
}

Enfin, vous pouvez désactiver les notifications avec removeUpdates en lui donnant le LocationListener ou le PendingIntent concerné. Si vous ne le faites pas, votre application continuera à recevoir des notifications après que tous les composants de l'application auront été fermés.

Les alertes de proximité

Dernière fonctionnalité que nous allons voir, le fait d'être informés quand on s'approche d'un endroit ou qu'on s'en éloigne. Cet endroit peut être symbolisé par un cercle dont on va préciser le centre et le rayon. Ainsi, si on entre dans ce cercle ou qu'on sort de ce cercle, l'alerte est lancée.

Le prototype de la méthode qui peut créer une alerte de proximité est void addProximityAlert(double latitude, double longitude, float radius, long expiration, PendingIntent intent) avec :

  • La latitude et la longitude du centre du cercle.
  • Le rayon d'effet est précisé dans radius en mètres.
  • expiration permet de déclarer combien de temps cette alerte est valable. Tout nombre en dessous de 0 signifie qu'il n'y a pas d'expiration possible.
  • Enfin, comme vous vous en doutez, on donne aussi le PendingIntent qui sera lancé quand cette alerte est déclenchée, avec intent.

Cette fois, l'intent contiendra un booléen en extra, dont la clé est KEY_PROXIMITY_ENTERING et la valeur sera true si on entre dans la zone et false si on en sort.

1
2
3
4
5
6
Intent intent = new Intent(this, AlertReceiver.class);

PendingIntent pending = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

// On ajoute une alerte de proximité si on s'approche ou s'éloigne du bâtiment de Simple IT
locationManager.addProximityAlert(48.872808, 2.33517, 150, -1, pending);

On le recevra ensuite dans :

1
2
3
4
5
6
7
public class AlertReceiver extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {
    // Vaudra true par défaut si on ne trouve pas l'extra booléen dont la clé est LocationManager.KEY_PROXIMITY_ENTERING
    bool entrer = booleanValue(intent.getBooleanExtra(LocationManager.KEY_PROXIMITY_ENTERING, true));
  }
}

Enfin, il faut désactiver une alerte de proximité avec void removeProximityAlert(PendingIntent intent).

Afficher des cartes

C'est bien de pouvoir récupérer l'emplacement de l'appareil à l'aide du GPS, mais il faut avouer que si on ne peut pas l'afficher sur une carte c'est sacrément moins sympa ! Pour cela, on va passer par l'API Google Maps.

Contrairement aux API que nous avons vues pour l'instant, l'API pour Google Maps n'est pas intégrée à Android, mais appartient à une extension appelée « Google APIs », comme nous l'avons vu au cours des premiers chapitres. Ainsi, quand vous allez créer un projet, au lieu de sélectionner Android 2.1 (API 7), on va sélectionner Google APIs (Google Inc.) (API 7). Et si vous ne trouvez pas Google APIs (Google Inc.) (API 7), c'est qu'il vous faudra le télécharger, auquel cas je vous renvoie au chapitre 2 de la première partie qui traite de ce sujet. Il faut ensuite l'ajouter dans le Manifest. Pour cela, on doit ajouter la ligne suivante dans le nœud application :

1
<uses-library android:name="com.google.android.maps" />

Cette opération rendra votre application invisible sur le Play Store si l'appareil n'a pas Google Maps. De plus, n'oubliez pas d'ajouter une permission pour accéder à internet, les cartes ne se téléchargent pas par magie :

1
<uses-permission android:name="android.permission.INTERNET" />

Obtenir une clé pour utiliser Google Maps

Pour pouvoir utiliser Google Maps, il vous faudra demander l'autorisation pour accéder aux services sur internet. Pour cela, vous allez demander une clé.

Comme vous pouvez le voir, pour obtenir la clé, vous aurez besoin de l'empreinte MD5 du certificat que vous utilisez pour signer votre application. Si vous ne comprenez pas un traître mot de ce que je viens de dire, c'est que vous n'avez pas lu l'annexe sur la publication d'applications, ce que je vous invite à faire, en particulier la partie sur la signature.

Ensuite, la première chose à faire est de repérer où se trouve votre certificat. Si vous travaillez en mode debug, alors Eclipse va générer un certificat pour vous :

  • Sous Windows, le certificat par défaut se trouve dans le répertoire consacré à votre compte utilisateur dans \.android\debug.keystore. Si vous avez Windows Vista, 7 ou 8, alors ce répertoire utilisateur se trouve par défaut dans C:\Users\nom_d_utilisateur. Pour Windows XP, il se trouve dans C:\Documents and Settings\nom_d_utilisateur.
  • Pour Mac OS et Linux, il se trouve dans ~/.android/.

Puis il vous suffira de lancer la commande suivante dans un terminal :

1
keytool -list -keystore "Emplacement du certificat" -storepass android -keypass android

Deux choses auxquelles vous devez faire attention :

  • Si cette commande ne marche pas, c'est que vous avez mal configuré votre PATH au moment de l'installation de Java. Ce n'est pas grave, il vous suffit d'aller à l'emplacement où se trouve l'outil keytool et d'effectuer la commande. Chez moi, il s'agit de C:\Program Files (x86)\Java\jre7\bin.
  • Et si comme moi vous utilisez Java 7 au lieu de Java 6, vous devrez rajouter -v à la commande, ce qui donne keytool -list -keystore "Emplacement du certificat" -storepass android -keypass android -v

Ainsi, ce que j'ai fait pour obtenir ma clé MD5, c'est :

  • Aller dans C:\Program Files (x86)\Java\jre7\bin ;
  • Taper keytool -list -keystore "C:\Users\Apollidore\.android\debug.keystore" -storepass android -keypass android -v ;
  • Repérer la ligne où il était écrit « MD5 », comme à la figure suivante.

Comme vous pouvez le voir, on évite de montrer à tout le monde les codes obtenus, sinon on pourrait se faire passer pour vous

Insérez ensuite le code MD5 sur le site pour obtenir une clé que vous pourrez utiliser dans l'application, mais nous verrons comment procéder plus tard.

L'activité qui contiendra la carte

Tout d'abord, pour simplifier l'utilisation des cartes, chaque activité qui contiendra une carte sera de type MapActivity. Vous aurez besoin d'implémenter au moins deux méthodes : onCreate(Bundle) et protected boolean isRouteDisplayed(). Cette méthode permet de savoir si d'une manière ou d'une autre la vue qui affichera la carte permettra de visualiser des informations de type itinéraire ou parcours. Renvoyez true si c'est le cas, sinon renvoyez false. Enfin, vous devez aussi implémenter la méthode protected boolean isLocationDisplayed() qui renverra true si votre application affiche l'emplacement actuel de l'utilisateur.

Vous devez implémenter ces deux méthodes pour utiliser légalement cette API.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import android.os.Bundle;

import com.google.android.maps.MapActivity;

public class MainActivity extends MapActivity {

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  }

  @Override
  protected boolean isRouteDisplayed() {
    return false;
  }

  @Override
  protected boolean isLocationDisplayed() {
    return true;
  }
}

La carte en elle-même

Pour gérer l'affichage de la carte, on va utiliser une MapView qui se trouve elle aussi dans le package com.google.android.maps. Au moment de la déclaration en XML, on va penser surtout à deux attributs :

  • android:clickable, si vous voulez que l'utilisateur puisse cliquer sur la carte ou se déplacer dans la carte ;
  • android:apiKey, pour préciser la clé que vous avez récupérée sur le site.

Par exemple :

1
2
3
4
5
<com.google.android.maps.MapView android:id="+id/mapView"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:clickable="true"
  android:apiKey="votre clé" />

Ne mettez pas plus d'une MapActivity et une MapView par application, ce n'est pas très bien supporté pour l'instant, elles pourraient entrer en conflit.

Comme vous le savez probablement, il existe trois modes d'affichage sur une carte Google Maps :

  • Si vous voulez afficher la vue satellitaire, utilisez mapView.setSatellite(true).
  • Si vous voulez afficher les routes, les noms des rues et tout ce qu'on peut attendre d'une carte routière, utilisez mapView.setTraffic(true).
  • Et si vous voulez afficher la possibilité de passer en mode Street View (vous savez, ce mode qui prend le point de vue d'un piéton au milieu d'une rue), alors utilisez mapView.setStreetView(true). Ce mode n'est pas compatible avec le mode trafic.

Le contrôleur

Votre carte affiche ce que vous voulez… enfin presque. Si vous voulez faire un zoom ou déplacer la région actuellement visualisée, il vous faudra utiliser un MapController. Pour récupérer le MapController associé à une MapView, il suffit de faire MapController getController().

Le zoom

Il existe 21 niveaux de zoom, et chacun d'eux représente un doublement dans le nombre de pixels qu'affiche une même zone. Ainsi en zoom 1, on peut voir toute la planète (plusieurs fois), alors qu'en zoom 21 on peut voir le chien du voisin. Pour contrôler le zoom, utilisez sur le contrôleur la méthode int setZoom(int zoomLevel) qui retourne le nouveau niveau de zoom. Vous pouvez zoomer et dézoomer d'un niveau avec respectivement boolean zoomIn() et boolean zoomOut().

Se déplacer dans la carte

Pour modifier l'emplacement qu'affiche le centre de la carte, il faut utiliser la méthode void setCenter(GeoPoint point) (ou public void animateTo(GeoPoint point) si vous voulez une animation). Comme vous pouvez le voir, ces deux méthodes prennent des objets de type GeoPoint. Très simplement, un GeoPoint est utilisé pour représenter un emplacement sur la planète, en lui donnant une longitude et une latitude. Ainsi, le GeoPoint qui représente le bâtiment où se situe Simple IT sera créé de cette manière :

1
2
3
4
5
// Comme les unités sont en microdegrés, il faut multiplier par 1E6
int latitude = 48.872808 * 1E6;
int longitude = 2.33517 * 1E6;

GeoPoint simpleIt = new GeoPoint(latitude.intValue(), longitude.intValue());

Ce qui est pratique, c'est que ce calque permet d'effectuer une action dès que le GPS détecte la position de l'utilisateur avec la méthode boolean runOnFirstFix(Runnable runnable), par exemple pour zoomer sur la position de l'utilisateur à ce moment-là :

1
2
3
4
5
6
overlay.runOnFirstFix(new Runnable() {
  @Override
  public void run() {
    mMapView.getController().animateTo(overlay.getMyLocation());
  }
});

Utiliser les calques pour afficher des informations complémentaires

Parfois, afficher une carte n'est pas suffisant, on veut en plus y ajouter des informations. Et si je veux afficher Zozor, la mascotte du Site du Zéro, sur ma carte à l'emplacement où se trouve Simple IT ? Et si en plus je voulais détecter les clics des utilisateurs sur un emplacement de la carte ? Il est possible de rajouter plusieurs couches sur lesquelles dessiner et qui sauront réagir aux évènements communs. Ces couches sont des calques, qui sont des objets de type Overlay.

Ajouter des calques

Pour récupérer la liste des calques que contient la carte, on fait List<Overlay> getOverlays(). Il suffit d'ajouter des Overlay à cette liste pour qu'ils soient dessinés sur la carte. Vous pouvez très bien accumuler les calques sur une carte, de manière à dessiner des choses les unes sur les autres. Chaque calque ajouté se superpose aux précédents, il se place au-dessus. Ainsi, les dessins de ce nouveau calque se placeront au-dessus des précédents et les évènements, par exemple les touchers, seront gérés de la manière suivante : le calque qui se trouve au sommet recevra en premier l'évènement. Si ce calque n'est pas capable de gérer cet évènement, ce dernier est alors transmis aux calques qui se trouvent en dessous. Néanmoins, si le calque est effectivement capable de gérer cet évènement, alors il s'en charge et l'évènement ne sera plus propagé.

Enfin, pour indiquer qu'on a rajouté un Overlay, on utilise la méthode void postInvalidate() pour faire se redessiner la MapView :

1
2
3
4
List<Overlay> overlays = mapView.getOverlays();
OverlayExample o = new OverlayExample();
overlays.add(o);
mapView.postInvalidate();

Dessiner sur un calque

Cependant, si vous voulez ajouter un point d'intérêt — on dit aussi un « POI » —, c'est-à-dire un endroit remarquable sur la carte, je vous recommande plutôt d'utiliser la classe ItemizedOverlay.

On implémente en général au moins deux méthodes. La première est void draw(Canvas canvas, MapView mapView, boolean shadow) qui décrit le dessin à effectuer. On dessine sur le canvas et on projette le dessin du Canvas sur la vue mapView. En ce qui concerne shadow, c'est plus compliqué. En fait, cette méthode draw est appelée deux fois : une première fois où shadow vaut false pour dessiner normalement et une seconde où shadow vaut true pour rajouter des ombres à votre dessin.

On a un problème d'interface entre le Canvas et la MapView : les coordonnées sur le Canvas sont en Point et les coordonnées sur la MapView sont en GeoPoint, il nous faut donc un moyen pour convertir nos Point en GeoPoint. Pour cela, on utilise une Projection avec la méthode Projection getProjection() sur la MapView. Pour obtenir un GeoPoint depuis un Point, on peut faire GeoPoint fromPixels(int x, int y) et, pour obtenir un Point depuis un GeoPoint, on peut faire Point toPixels(GeoPoint in, Point out). Par exemple :

1
2
3
4
5
6
7
Point point = new Point(10, 10);
// Obtenir un GeoPoint
GeoPoint geo = projection.fromPixels(point.x, point.y);

Point nouveauPoint = null;
// Obtenir un Point. On ignore le retour pour passer l'objet en paramètre pour des raisons d'optimisation
projection.toPixels(geo, nouveauPoint );

Ainsi, pour dessiner un point rouge à l'emplacement du bâtiment de Simple IT :

 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
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.RectF;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;


public class PositionOverlay extends Overlay {
  private int mRadius = 5;

  // Coordonnées du bâtiment de Simple IT
  private Double mLatitutde = 48.872808*1E6;
  private Double mLongitude = 2.33517*1E6;

  public PositionOverlay() {

  }

  @Override
  public void draw(Canvas canvas, MapView mapView, boolean shadow) {
    Projection projection = mapView.getProjection();

    GeoPoint geo = new GeoPoint(mLatitutde.intValue(), mLongitude.intValue());

    if (!shadow) {
      Point point = new Point();

      // Convertir les points géographiques en points pour le Canvas
      projection.toPixels(geo, point);

      //  Créer le pinceau
      Paint paint = new Paint();
      paint.setARGB(255, 255, 0, 0);

      // Création du cercle
      RectF cercle = new RectF(point.x - mRadius, point.y - mRadius, point.x + mRadius, point.y + mRadius);

      // Dessine le cercle
      canvas.drawOval(cercle, paint);
    }
  }

}

Gérer les évènements sur un calque

La méthode de callback qui sera appelée quand l'utilisateur appuiera sur le calque s'appelle boolean onTap(GeoPoint p, MapView mapView) avec p l'endroit où l'utilisateur a appuyé et mapView la carte sur laquelle il a appuyé. Il vous est demandé de renvoyer true si l'évènement a été géré (auquel cas il ne sera plus transmis aux couches qui se trouvent en dessous).

1
2
3
4
5
6
7
8
@Override
public boolean onTap(GeoPoint point, MapView mapView) {
  if (/** Test **/) {
    // Faire quelque chose
    return true;
  }
  return false;
}

Quelques calques spécifiques

Maintenant que vous pouvez créer tous les calques que vous voulez, nous allons en voir quelques-uns qui permettent de nous faciliter grandement la vie dès qu'il s'agit de faire quelques tâches standards.

Afficher la position actuelle

Les calques de type MyLocationOverlay permettent d'afficher votre position actuelle ainsi que votre orientation à l'aide d'un capteur qui est disponible dans la plupart des appareils de nos jours. Pour activer l'affichage de la position actuelle, il suffit d'utiliser boolean enableMyLocation() et, pour afficher l'orientation, il suffit d'utiliser boolean enableCompass(). Afin d'économiser la batterie, il est conseillé de désactiver ces deux fonctionnalités quand l'activité passe en pause, puis de les réactiver après :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public void onResume() {
  super.onResume();
  location.enableCompass();
  location.enableMyLocation();
}

public void onPause() {
  super.onPause();
  location.disableCompass();
  location.disableMyLocation();
}

Ajouter des marqueurs

Pour marquer l'endroit où se trouvait le bâtiment de Simple IT, nous avons ajouté un rond rouge sur la carte, cependant il existe un type d'objets qui permet de faire ce genre de tâches très facilement. Il s'agit d'OverlayItem. Vous aurez aussi besoin d'ItemizedOverlay qui est une liste d'OverlayItem à gérer et c'est elle qui fera les dessins, la gestion des évènements, etc.

Nous allons donc créer une classe qui dérive d'ItemizedOverlay<OverlayItem>. Nous aurons ensuite à nous occuper du constructeur. Dans celui-ci, il faudra passer un Drawable qui représentera le marqueur visuel de nos points d'intérêt. De plus, dans le constructeur, il vous faudra construire les différents OverlayItem et déclarer quand vous aurez fini avec la méthode void populate() :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class ZozorOverlay extends ItemizedOverlay<OverlayItem> {
  private List<OverlayItem> mItems = new ArrayList<OverlayItem>();

  public ZozorOverlay(Drawable defaultMarker) {
    super(defaultMarker);

    Double latitude = 48.872808*1E6;
    Double longitude = 2.33517*1E6;
    mItems.add(new OverlayItem(new GeoPoint(latitude.intValue() , longitude.intValue()), "Simple IT", "Maison du Site du Zéro"));
    populate();
  }

  // …
}

Vous remarquerez qu'un OverlayItem prend trois paramètres :

  • Le premier est le GeoPoint où se trouve l'objet sur la carte.
  • Le deuxième est le titre à attribuer au point d'intérêt.
  • Le dernier est un court texte descriptif.

Enfin, il existe deux autres méthodes que vous devez implémenter :

  • int size(), qui retourne le nombre de points d'intérêt dans votre liste.
  • Item createItem(int i), pour retourner le i-ième élément de votre liste.

  • Il existe plusieurs manières de détecter la position d'un appareil. La plus efficace est la méthode qui exploite la puce GPS, mais c'est aussi la plus consommatrice en énergie. La seconde méthode se base plutôt sur les réseaux Wifi et les capteurs internes du téléphone. Elle est beaucoup moins précise mais peut être utilisée en dernier recours si l'utilisateur a désactivé l'utilisation de la puce GPS.
  • Il est possible de récupérer la position de l'utilisateur avec le LocationManager. Il est aussi possible de créer des évènements qui permettent de réagir au statut de l'utilisateur avec LocationListener.
  • Il est possible d'afficher des cartes fournies par Google Maps avec l'API Google Maps. Ainsi, on gérera une carte avec une MapActivity et on l'affichera dans une MapView.
  • Le plus intéressant est de pouvoir ajouter des calques, afin de fournir des informations basées sur la géographie à l'utilisateur. On peut par exemple proposer à un utilisateur d'afficher les restaurants qui se trouvent à proximité. Un calque est un objet de type Overlay.