Android, A Complete Course, From Basics to Enterprise Edition

Géolocalisation et MapView

Android, A Complete Course, From Basics to Enterprise Edition. 1

Géolocalisation et MapView.. 1

Communiquer avec le système. 1

1.1         La géo-localisation. 1

1.2         MapView et MapActivity. 5

 Cet article est extraie du livre « Android, A Complete Course », disponible sur Android2ee.com.

Les exemples ou les programmes présents dans cet ouvrage sont fournis pour illustrer les descriptions théoriques. Ce code est libre de toute utilisation mais n'est pas distribuable.

La distribution du code est reservée au site :

http://android2ee.com.

La propriété intellectuelle du code appartient à :

Mathias Séguy

L’utilisation de ces codes est sous votre unique responsabilité et personne d’autre que vous ne pourra être tenu responsable des préjudices ou dommages de quelques natures que ce soit pouvant résulter de son utilisation.

Tous les noms de produits ou marques cités dans cet ouvrage sont des marques déposés par leurs propriétaires respectifs.

Publié par http://android2ee.com

Titre Original : Android, A Complete Course, From Basics to Enterprise Edition. Édition Française.

ISBN : 979-10-90388-00-0

Copyright © 2011 by Mathias Séguy

 

Aucune représentation ou reproduction, même partielle, autre que celles prévues à l’article L. 122-5 2° et 3° a) du code de la propriété intellectuelle ne peut être faite sans l’autorisation expresse de Mathias Seguy ou, le cas échéant, sans le respect des modalités prévues à l’article L. 122-10 dudit code

Communiquer avec le système

1.1       La géo-localisation

La géo localisation peut être effectuée soit :

·         grâce au GPS (le plus courant),

·         grâce à Galileo, dont la mise en œuvre est en cours (version européenne du GPS),

·         grâce à la triangulation,

·         grâce à un hot spot wifi, lui-même géo localisé.

Il y a trois objets à connaitre quand on fait de la géo localisation :

·         Le LocationManager qui gère les LocationProvider, vous permet de les récupérer, de les lister, d’utiliser une liste de critères pour récupérer celui qui mappe le plus à ces derniers…

·         Le LocationProvider qui vous fournit la Location, vous permet de connaître sa précision (Accuracy), son nom, s’il est payant, s’il nécessite un réseau téléphonique, un réseau internet, un réseau satellite, s’il fournit l’altitude, la boussole ou la vitesse.

·         La Location qui donne la longitude, la latitude, l’altitude, la boussole (votre degré relativement au nord), la précision, la vitesse, l’heure de la dernière localisation… Toutes ces données ne sont pas toujours disponibles, on peut récupérer null ou vérifier avec has**.

Une chose à savoir, aussi, c’est que l’on n’obtient pas sa position en temps réel, mais que l’on demande la dernière position connue. La plupart du temps, votre application s’enregistre comme listener d’un changement de localisation.

Enfin, certains provider possède un attribut Bundle qui lui permet de stocker d’autres informations, à vous de les découvrir.

Enfin, la classe location possède des méthodes de calcul de distance.

1.1.1       Les permissions

Il faut demander les permissions pour utiliser ces services :

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

                package="com.paad.whereami">

                <application android:icon="@drawable/icon">

                               <activity android:name=".MyGeoActivity" android:label="@string/app_name">

                                               <intent-filter>

                                                               <action android:name="android.intent.action.MAIN" />

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

                                               </intent-filter>

                                </activity>

                </application>

                <!--         Permission to access GPS permission-->

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

                <!--         Permission to access WIFI or cellular network localization permission-->

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

                <!--         Permission to access internet (usual)-->

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

</manifest>

Et pour les tests, ACCESS_MOCK_LOCATION peut être utile comme permission.

1.1.2       Exemple d’une utilisation simplifiée

Dans cet exemple, on utilise le location manager pour obtenir le location provider avec son nom. Puis on lui demande la dernière localisation connue et on l’affiche :

 

//Recupération du location manager

LocationManager locationManager;

String context = Context.LOCATION_SERVICE;

locationManager = (LocationManager)getSystemService(context);

//for using GPS

String provider = LocationManager.GPS_PROVIDER;

//for WIFI or cellular network

//provider = LocationManager.NETWORK_PROVIDER;

Location location =

  locationManager.getLastKnownLocation(provider);

printLocation(location);

où :

private String printLocation(Location location) {

String strLocation;

if (location != null) {

  double latitude = location.getLatitude();

  double longitude = location.getLongitude();

  float accuracy=location.getAccuracy();

  double altitude=location.getAltitude();

  float bearing=location.getBearing();

  float speed=location.getSpeed();

  long time=location.getTime();

  strLocation = "Votre location est : \nLatitude: " + latitude +

  "\nLongitude: " + longitude+

  "\nAltitude: " + altitude+

  "\nVotre angle (en degrès) relativement au nord:" + bearing+

  "\nVotre vitesse est: " + speed+

  "\nCes informations sont valables avec une précision (en mètres) de :" + accuracy+

  "\nCes informations date du " + new Date(time).toString();

} else {

    strLocation = "Aucune position trouvée";

 }

 return strLocation;

}

1.1.3       Exemple d’une utilisation avec critère de recherche du provider

Dans cet exemple nous utilisons des critères pour obtenir la localisation provider le plus proche de nos exigences :

//Recupération du location manager

LocationManager locationManager;

String context = Context.LOCATION_SERVICE;

locationManager = (LocationManager)getSystemService(context);

//Utilisation de l’objet critères

Criteria criteria = new Criteria();

//Je souhaite une precision fine

criteria.setAccuracy(Criteria.ACCURACY_FINE);

//Je n’utilise pas l’altitude

criteria.setAltitudeRequired(false);

//ni la boussole

criteria.setBearingRequired(false);

//et je me fiche que ce soit un service payant

criteria.setCostAllowed(true);

//Par contre, je souhaite que ce soit peu gourmant en énergie

criteria.setPowerRequirement(Criteria.POWER_LOW);

//allez renvoie moi, le LocationProvider qui répond le plus à mes critères

String provider = locationManager.getBestProvider(criteria, true);

//Si j’avais voulu le provider lui-même :

LocationProvider locationProvider=locationManager.getProvider(provider);

//mais ce n’est pas la peine pour obtenir la dernière localisation

Location location = locationManager.getLastKnownLocation(provider);

1.1.4       Exemple d’utilisation avec la mise en place d’un listener de changement de localisation

Dans cet exemple on montre comment s’enregistrer en tant que listener d’un changement de localisation :

//Recupération du nom du LocationProvider

String provider = locationManager.getBestProvider(criteria, true);

//je m’enregistre en tant que listener d’un changement de localisation :

// requestLocationUpdates (nomDuProvider,temps minimal avant d’être prévenu de nouveau en milliseconde, //distance minimale de mouvement avant d’être prévenu de nouveau, monlistener)

locationManager.requestLocationUpdates(provider, 1974, 10, locationListener);

//fin de cette méthode

 }

/**

 * Mon listener de changement de localisation

 */

  private final LocationListener locationListener = new LocationListener() {

    //Est appelé dés que la localisation change (attention cela prend en compte vos paramètres minTime et minDistance quand il s’est enregistré.

    public void onLocationChanged(Location location) {

      updateWithNewLocation(location);

    }

    //Est appelé quand le provider est arrété

    public void onProviderDisabled(String provider){

      updateWithNewLocation(null);

    }

    //Est appelé quand le provider est de nouveau opérationnel

    public void onProviderEnabled(String provider){ }

    //Est appelé quand le provider change de statu passage de enable a disable ou quand il n’arrive pas  

    // a chercher sa localisation

    public void onStatusChanged(String provider, int status,

                                Bundle extras){ }

  };

 

//A utiliser dans la méthode onDestroy (ou ailleurs pour dé inscruire votre listener quand votre application a fini :

locationManager.removeUpdates(locationListener)

1.1.5       Une alerte quand on se rapproche de notre cible

Android détecte la présence de l’appareil dans une zone que vous fixez. Cette zone est un cercle. Attention toute fois, n’oubliez pas qu’Android n’est pas en permanence en train de se localiser, alors ne mettez pas de rayon à votre cercle trop petit, sous peine de passer dans votre zone sans en être informé.

Pour utiliser ce système d’alerte, il faut utiliser les Intents pour récupérer l’évenement (cf le chapitre 5.3 Filtres d’intentions). Ensuite, voilà le code à utiliser. Le listener est le même que celui de l’exemple ci-dessus. Il sera prévenu quand vous êtes dans la zone, et tant que vous y restez.

    //Usual intent call with this

    Intent intent = new Intent(this, this.getClass());

    PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, 0);

    //Using the usual Intent process with theIntent name

    Intent intent2 = new Intent("MyIntentCompleteName");

    PendingIntent pIntent2 = PendingIntent.getBroadcast(this, 0, intent, 0);

    //And then register: (latitude, longitude, radius, expiration, intent)

    locationManager.addProximityAlert(43.565715431592736d, 1.398482322692871d, 100, 1000000, pIntent);

où Latitude Longitude est le centre du cercle et radius son rayon. L’expiration est le temps pour lequel votre alerte est valable (je veux être alerté pendant n millisecondes (ou -1 pour que ce soit permanent).

N’oubliez pas de vous désenregistrer :

locationManager.removeProximityAlert(pIntent);

1.1.6       Le géocodage

Le géo codage permet à partir d’une adresse d’obtenir la localisation associée, et vis-versa. Comme toujours, utiliser une thread ou un handler pour déporter le traitement dans une thread indépendante.

//First of all ask the permission in the manifest to go to internet

// Instancing the GeoCoder using this as Context (here this=my Activity) and my Locale.

Geocoder geoCoder = new Geocoder(this, Locale.FRANCE);

// Retrieve Adresses from location:

List<Address> adresses = null;

Address address;

double latitude = 43.565715431592736d;

double longitude = 1.398482322692871d;

int maxResults = 10;

StringBuilder strBuild=new StringBuilder();

try {

                adresses = geoCoder.getFromLocation(latitude, longitude, maxResults);

                address = adresses.get(0);

                if(address!=null){

strBuild.append(address.getAddressLine(1));

strBuild.append(address.getPostalCode());

strBuild.append(address.getLocality());

strBuild.append(address.getCountryName());

//Do something with the string

                }

} catch (IOException expe) {

                // Do something

}

 

// Retrive Location from an Adress

String myAdress="65 allée de bellefontaine, 31100, Toulouse, France";

try {

                adresses=geoCoder.getFromLocationName(myAdress, maxResults);

                address = adresses.get(0);

                if(address!=null){

strBuild.append("The associated latitude is : ");

strBuild.append(address.getLatitude());

strBuild.append("and the longitude is : ");

strBuild.append(address.getLongitude());

//Do something with the string

                }

}catch (IOException expe) {

                // Do something

}

1.2       MapView et MapActivity

Ce ne sont pas des composants obligatoires de la plate forme. Vous devez donc vérifier leur existence sur l’appareil.

De plus sachez qu’il vous faut vous enregistrer auprès de Google pour obtenir une clef qui vous permette d’utiliser GoogleMap (en fait deux clefs, l’un pour la production, l’autre pour le développement) : http://code.google.com/intl/fr/android/maps-api-signup.html et lisez bien la licence. Sinon vous pouvez vous tournez vers OpenStreetMap.

Les objets principaux pour pouvoir manipuler et afficher les cartes sont :

·         MapActivity (qui s’apparente à ListActivity pour les activités voulant être des listes) et qui est la classe à étendre pour que votre Activity puisse afficher et gérer les cartes.

·         MapView qui est l’objet graphique carte (comme TextView est l’objet Text).

·         MapController qui vous permettra de gérer le comportement de votre MapView (zoomer, translater,…)

Ensuite les objets qui vous permettront d’afficher des informations supplémentaires sur votre carte (et qui doivent être considérés comme des calques que vous superposez les uns aux autres) sont :

·         MyLocationOverlay qui est un calque prédéfini permettant d’afficher votre position courante (et votre direction)

·         Overlay qui est la classe à étendre pour pouvoir définir vos propres calques (si vous ne voulez que rajouter des Items utilisez plutôt ItemzedOverlays)

·         ItemizedOverlays qui est la classe à étendre si vous voulez ajouter des Items : des OverlaysItems et que vous ne souhaitez pas effectuer d’autre cutomisation.

1.2.1       Afficher la carte

Il y a trois éléments à modifier pour pouvoir le faire : le fichier manifest.xml, votre fichier de layout (main.xml) et enfin votre activité :

1.2.1.1       Le fichier manifest.xml

Il faut spécifier que vous souhaitez utiliser la librairie com.google.android.maps et que vous souhaitez accéder à internet.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

  package="net.stinfoservices.net.android"

  android:versionCode="1"

  android:versionName="1.0">

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

  <application android:icon="@drawable/icon" android:label="@string/app_name">

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

    <activity android:name=".StiMap" android:label="@string/app_name">   

      <intent-filter>

        <action android:name="android.intent.action.MAIN" />

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

      </intent-filter>

    </activity>

  </application>

  <uses-sdk android:minSdkVersion="7" />

</manifest>

1.2.1.2       Le fichier de layout, main.xml

Il vous faut déclarer l’objet graphique map :

<com.google.android.maps.MapView

    android:id="@+id/map_view"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    android:enabled="true"

    android:clickable="true"

    android:apiKey="myapikey"

  />

Le chapitre 14.3 Retrouver sa clef pour  vous permet de récupérer la clef de l’API à positionner dans le tag android:apiKey.

1.2.1.3       L’activité

Faisons-nous plaisir avec la carte :

public class StiMap extends MapActivity {

  /** The map View that displays the map */

  MapView myMap;

  /** The overLay that displays your position */

  MyLocationOverlay myPosOverlay;

 

  /** Called when the activity is first created. */

  @Override

  public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    myMap = (MapView) findViewById(R.id.map_view);

 

    /** Setting my information displayed on the map */

    // Show satellite

    myMap.setSatellite(true);

    // Show street View

    myMap.setStreetView(false);

    // show traffic: You have to choose between streetView and Traffic

    myMap.setTraffic(true);

 

    /** Display map controller to the user */

    // Zoom controller buttons (the boolean is TakeFocus)

    myMap.displayZoomControls(true);

    // ou celui ci (the boolean is show zoom buttons control)

    myMap.setBuiltInZoomControls(true);

    // The scroll

    myMap.setScrollbarFadingEnabled(true);

    myMap.setScrollContainer(true);

 

    /**

     * Getting information on the displayed area (help you to focus your search using GeoCoding)

     */

    // The current zoom (1 for full earth and 21 for biggest available zoom)

    int currentZoom = myMap.getZoomLevel();

    // The center point of the Map (the GeoPoint is the object used for localized point)

    // location.lat*1E6=geoPoint.lat (the same for longitude)

    GeoPoint center = myMap.getMapCenter();

    // The current latitude span (from the top edge to the bottom edge of the map) in decimal

    // degrees multiplied by one million (1000000).

    int latSpan = myMap.getLatitudeSpan();

    // The current longitude span (from the left edge to the right edge of the map) in decimal

    // degrees multiplied by one million(1000000).

    int lonSpan = myMap.getLongitudeSpan();

 

    /** Controlling the displayed area */

    // Getting the Map Controller

    MapController mapController = myMap.getController();

    // Define where you want to go

    Double realLatitude = 43.565715431592736d;

    Double reallLongitude = 1.398482322692871d;

    int geoLatAssociated = (int) (realLatitude * 1E6);

    int geoLongAssociated = (int) (reallLongitude * 1E6);

    GeoPoint geoPoint = new GeoPoint(geoLatAssociated, geoLongAssociated);

    // You can animate your map's movement

    mapController.animateTo(geoPoint);

    // Or just go

    mapController.setCenter(geoPoint);

    // You can set a zoomFactor (between 1 and 21)

    int zoomLevel = 1;// World vision

    mapController.setZoom(zoomLevel);

    // or just Zoom in or out

    mapController.zoomIn();

    mapController.zoomOut();

    // And you can define the square you want to display

    mapController.zoomToSpan(latSpan, lonSpan);

    // You can translate your view (but its in pixel)

    int x = 3, y = 4;

    mapController.scrollBy(x, y);

 

    /** Managing the conversion between pixel and latitude/longitude */

    Projection proj = myMap.getProjection();

    // Retrieve the GeoPoint associated to the pixel position (x,y)

    geoPoint = proj.fromPixels(x, y);

    // Converts the given GeoPoint to onscreen pixel coordinates,

    // relative to the top-left of the MapView that provided this Projection:

    // in - The latitude/longitude pair to convert.

    // out - A pre-existing object to use for the output; if null, a new Point will be allocated

    // and returned.

    GeoPoint geoPointToConvert = geoPoint;

    Point point = new Point();

    proj.toPixels(geoPoint, point);

 

    /** Adding OverLays */

    // My own overlay

    // First retrieve the image you want to use to mark your item location on the map

    Resources res = getResources();

    Drawable drawable = res.getDrawable(R.drawable.myMarker);

    MyItemizedOverlay myOverlay = new MyItemizedOverlay(drawable);

    myMap.getOverlays().add(myOverlay);

    // The standard MyLocationOverlay(Context,MapView)

    // here the context is this (==Current Acitivity)

    myPosOverlay = new MyLocationOverlay(this, myMap);

    myMap.getOverlays().add(myPosOverlay);

 

  }

 

  /*  * (non-Javadoc)   *

   * @see com.google.android.maps.MapActivity#onPause()   */

  @Override

  protected void onPause() {

    super.onPause();

    // As you added the MyLocationOverlay and shut it down in the onResume

    // you have to wake it again

    myPosOverlay.enableMyLocation();

    myPosOverlay.enableCompass();

  }

 

  /** (non-Javadoc) *

   * @see com.google.android.maps.MapActivity#onResume()   */

  @Override

  protected void onResume() {

    super.onResume();

    // As you added the MyLocationOverlay you have to shut it down it

    // to optimize ressources, power and cpu

    myPosOverlay.disableMyLocation();

    myPosOverlay.disableCompass();

 

  }

 

  /* * (non-Javadoc)   *

   * @see com.google.android.maps.MapActivity#isRouteDisplayed()   */

  @Override

  protected boolean isRouteDisplayed() {

    // Return true if you want your Activity to display direction

    // Return false else

    return false;

  }

}

1.2.2       Créer son Overlays qui étend ItemizedOverlay

Créer un overlay qui dérive d’ItemizedOverlay est assez trivial :

public class MyItemizedOverlay extends ItemizedOverlay<OverlayItem> {

/** The item list to display*/

  private List<OverlayItem> items = new ArrayList<OverlayItem>();

 

  /**

   * @param defaultMarker

   */

  public MyItemizedOverlay(Drawable defaultMarker) {

    super(defaultMarker);

    Double latitude = 43.565715431592736d * 1E6;

    Double longitude = 1.398482322692871d * 1E6;

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

    OverlayItem item = new OverlayItem(geoPoint, "My Entreprise", "St Info Services");

    items.add(item);

    // ... and so long for all the items you need to add

 

    // but never ever forget to call populate when you add or remove an item

    populate();

  }  /*****************************************************************************************/

/** Method to implements ******************************************************************/ /*****************************************************************************************/

  /*  * (non-Javadoc)   *

   * @see com.google.android.maps.ItemizedOverlay#createItem(int)   */

  @Override

  protected OverlayItem createItem(int i) {

    return items.get(i);

  }

  /*  * (non-Javadoc)   *

   * @see com.google.android.maps.ItemizedOverlay#size()  */

  @Override

  public int size() {

    return items.size();

  }  /*****************************************************************************************/

/** Methods to override for having a specific behavior ************************************/  /****************************************************************************************/

  /*  * (non-Javadoc)   *

   * @see com.google.android.maps.ItemizedOverlay#onTap(int)   */

  @Override

  protected boolean onTap(int index) {

    // This method is called when the user touch your item

    // So do what you want (a toast for exemple)

    // or do nothing:

    return super.onTap(index);

  }

}