Tutoriel sur l'intégration de nos propres cartes dans Google Map

Il arrive souvent que pour des projets nous ayons la volonté d'offrir à l'utilisateur la carte la plus appropriée à son besoin, pour cela l'intégration de cartes spécifiques au sein de l'application devient nécessaire. Cela permet également d'avoir la possibilité d'afficher une carte sans avoir l'accès au réseaux ( hors ligne ).

Article lu   fois.

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Google Map v2 pour Android

Google Map v2 pour Android est une API fournie par Google pour pouvoir travailler sur des fonds de cartes. Son installation et utilisation restant très simple.

I-A. Certificat

Avant d'enregistrer votre application sur le Google Play Console, il vous faut récupérer/créer votre signature SHA-1, via la commande keytool.

Vous aurez besoin d'une clé lors du développement de votre application et d'une autre lors de la release quand vous aurez signé votre application.

I-A-1. En debug

Linux / OS X

 
Sélectionnez
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android

Windows

 
Sélectionnez
keytool -list -v -keystore "%USERPROFILE%\.android\debug.keystore" -alias androiddebugkey -storepass android -keypass android

I-A-2. Release

 
Sélectionnez
keytool -list -v -keystore your_keystore_name -alias your_alias_name

Vous obtiendrez alors votre clé SHA-1, votre MD5, ainsi que les paramètres de votre certificat.

I-B. Installation


Avant de commencer à utiliser l'API Google Map v2 pour Android vous devrez savoir que cette API nécessite l'installation sur l'appareil de Google Play Service. De ce fait vous aurez également besoin depuis votre code d'ajouter la bibliothèque google play service à votre projet.

Ensuite vous devrez récupérer la clé de l'API depuis Google APIs Console :

  1. depuis la page Services, vérifier que vous avez sélectionné "Google Maps Android API v2" ;
  2. ensuite depuis  le menu de navigation, cliquez sur API Access ;
  3. vous trouverez alors un bouton “Create New Android Key”, cliquez dessus ;
  4. une fenêtre de dialogue apparaîtra vous demandant d'entrer votre signature SHA-1, un point-virgule et le nom de paquet de votre application. Par exemple :

XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX;com.android2ee.tileprovider

  1. vous obtiendrez ensuite une clé générée que vous devrez sauvegarder enfin de l'utiliser depuis votre application.

I-C. Déploiement

Pour utiliser Google Map v2 pour Android vous devez  avoir certaines informations dans votre application. Nous allons les détailler par type.

I-C-1. Le fichier Manifest

Dans le fichier Manifest vous aurez besoin de rajouter ces permissions :

 
Sélectionnez
1.
2.
3.
4.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>

Sans celles-ci, vous ne pourrez pas utiliser l' API Google Map.

Ensuite il vous sera nécessaire de rajouter également ces méta-données depuis votre application :

  1. la clé de l' API que vous avez générée préalablement,
  2. et de spécifier quelle version de la bibliothèque google play service vous utilisez.
 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
<!-- Google API Key --> 
<meta-data 
android:name="com.google.android.maps.v2.API_KEY" 
android:value="your_key" /> 
<meta-data 
android:name="com.google.android.gms.version" 
android:value="@integer/google_play_services_version" />

I-C-2. XML

Vous pouvez déclarer le composant graphique de Google Map depuis le fichier XML, pour cela il suffit d'intégrer dans le layout souhaité le composant graphique MapFragment (com.google.android.gms.maps.MapFragment)  ou SupportMapFragment (com.google.android.gms.maps.SupportMapFragment) si vous êtes pré-HoneyComb ou si vous souhaitez être compatibles toutes versions (bref inclure Gingerbread).

Ce composant graphique est basé sur un Fragment classique d'Android.

Dans cet exemple nous avons pris la SupportMapFragment :

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

Vous pouvez également charger dynamiquement ce composant graphique.

I-C-3. JAVA

l ne vous reste plus qu'à récupérer ce composant graphique depuis votre code JAVA en vue de le configurer pour diverse utilisation. Cela se fait tout simplement :

 
Sélectionnez
GoogleMap map = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)) .getMap();

De même pour une MapFragment :

 
Sélectionnez
GoogleMap map = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)) .getMap();

I-C-4. Autre informations

Attention il vous sera aussi utile de tester, si le Service de Google Play est disponible :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
// Getting Google Play availability status
int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(getBaseContext());
// Showing status
if(status!=ConnectionResult.SUCCESS){ // Google Play Services are not available
     Dialog dialog = GooglePlayServicesUtil.getErrorDialog(status, this, requestCode);
     dialog.show();
}

Cette partie est à réaliser avant de charger la Map, si vous n'arrivez pas à accéder au Service GooglePlay, affichez une information de type Dialog, DialogFragment, bandeau, etc. De plus, il vous est indispensable de proposer à l'utilisateur la documentation de Google sur l'utilisation des cartes. Pour cela le Service GooglePlay vous donne accès à cette documentation via ce code :

 
Sélectionnez
GooglePlayServicesUtil.getOpenSourceSoftwareLicenseInfo(context);

Vous pouvez par exemple proposer cette documentation via un menu depuis la barre d'action qui pourra se trouver dans l'overflow ( les trois petits points ). Le but étant que l'utilisateur ai accès à cette documentation depuis l'affichage de la carte sans être intrusif.

I-D. Support des cartes à intégrer

Maintenant que nous avons vu comment intégrer Google Map v2 pour Android quel type de support pour nos cartes pouvons nous trouver ?

I-D-1. MbTiles

Le format MbTiles est une base de donnée SQLite contenant les tuiles de la carte que nous voulons afficher. Cette base de donnée respecte un schéma suivant  :

  • une table metadata

    • name: son nom.
    • type: overlay ou baselayer
    • version: sa version.
    • description: une description.
    • format: le format des images: png ou jpg
    • bounds : le contour en latitude et longitude de la carte étendue. Elle est définie dans le format gauche, bas, droite, haut. Par exemple pour la Terre : -180.0, -85.0, 180.0, 85.0.
    • attribution : une chaîne de caractère qui détaillent les données présentent de la carte.
  • une table tiles

    • zoom_level : le niveau de zoom demandée
    • tile_column : la colonne demandée
    • tile_row : la ligne demandée
    • tile_data : l'image demandée

Vous pouvez aussi avoir deux tables optionnelles grid et grid data. La documentation ici.

I-D-2. Vector Tiles

C'est le support utilisé par Google Maps Android depuis Décembre 2010, cela permet de construire l'image avec des données vectorielles reliées au contour de chaque tuile au lieu d'avoir une image prédéfinie pour celle-ci.

Cela peut être intéressant, si par exemple vous fonctionnez avec un serveur de données, cela allégera les données envoyées par celui-ci, puisque c'est des images vectorielles qui seront envoyées et non le rendu de celles-ci.

I-D-3. Fichier

Vous pouvez tout simplement gérer vos tuiles par un système de fichier que vous aurez mis en place. Soit gérer par leur nom ou via un fichier (XML, JSON, etc ..) pour répertorier vos images.

Par exemple juste par le nom :

  • dossier-zoom1

    • image-1-1
  • dossier-zoom2

    • image-1-1
    • image-1-2
    • image-2-1
    • image-2-2
  • etc...

I-D-4. Autres

Il existe d'autres supports comme un serveur envoyant les images en fonction des paramètres, etc. Vous pouvez aussi créer vos propres moyens de communication !

II. Fonctionnement

II-A. TileOverlays

Le TilesOverlays permet de pouvoir intégrer une carte personnalisée dans GoogleMaps, Elle est utile quand nous voulons remplacer l'ensemble de la carte, soit une grande superficie. Si vous ne souhaitez modifier qu'une légère partie, la classe GroundOverlays sera plus intéressante.

TileOverlays : un ensemble d'images;

GroundOverlays : une image.


Comment fonctionne l'affichage de nos propres cartes au sein de Google Map v2 :

Image non disponible

L'initialisation se fait par un TileOverlay sur lequel nous associerons une TileProvider qui fournira les images en fonction du zoom, de la colonne et de la ligne voulus. Pour créer ce TileProvider nous aurons besoin d'un TileOverlayOption.

Voici ce que cela donnera dans le code :

 
Sélectionnez
GoogleMap map; // ... get a map.
TileProvider tileProvider; // ... create a tile provider.
TileOverlay tileOverlay = map.addTileOverlay(
    new TileOverlayOptions().tileProvider(tileProvider));
Image non disponible

Suite à l'initialisation, lorsque Google Maps affichera une tuile, celle-ci demandera au TileOverlay la tuile correspondante, si elle n'est pas crée alors une demande au TileProvider sera effectuée pour créer cette tuile. Cette demande s'effectuera par la fonction getTile fournie par le TileProvider.

Il existe deux classes pour gérer les tuiles , soit la TileProvider ou la UrlTileProvider.

La TileProvider fournie comme nous avons vu préalablement la fonction permettant de récupérer la tuile par la fonction getTile. ( la tuile se présentera sous le format Tile qui possède le contenu de l'image à afficher).

La taille : Les tuiles seront de taille de 256 * 256 dp comme indiqué dans la documentation et seront définies par leurs coordonnées ainsi que leur niveau de zoom. Voici un exemple de représentation de ces coordonnées :

Image non disponible

Pour le niveau de zoom celui sera défini par défaut à 0 jusqu'à n, il faut voir l'ensemble des tuiles comme une représentation de couches empilées ou chaque couche représente un niveau de zoom supplémentaire. Donc si nous combinons les coordonnées ainsi que le niveau de zoom. Nous aurons le quadruple de tuiles sur le zoom z+1 par rapport au zoom. Les coordonnées redémarre à chaque fois à 0,0 en haut à droite de la carte. Par exemple pour le zoom 0 nous aurons une seule image (1x1), pour le zoom 1, quatre  images (2x2), pour le zoom 2 , seize images (4x4), etc...


information :l'UrlTileProvider quant à elle fournit la possibilité de retourner une URL et non l'image depuis la fonction getTile. Cet URL correspondra à l'image à afficher en fonction des paramètres donnés. La requête au serveur et la récupération de l'image se fera automatiquement par la classe UrlTileProvider. Vous avez donc une classe simple vous permettant de récupérer des images sur un serveur.

II-B. Exemple par l'utilisation d'un MbTiles

Prenons un exemple pour illustrer ceci.  

II-B-1. Le fichier Manifest

Tout d'abord le fichier manifest, ou nous allons déclarer les permissions nécessaire à Google Maps, ainsi  que les metadatas.

Penser également à inclure le bibliothèque google play service à votre projet !

Tout d'abord le fichier manifest, ou nous allons déclarer les permissions nécessaire à Google Maps, ainsi  que les metadatas.

Penser également à inclure le bibliothèque google play service à votre projet !

 
Sélectionnez
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.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.android2ee.tileprovider"
   android:versionCode="1"
   android:versionName="1.0" >

   <uses-sdk
       android:minSdkVersion="8"
       android:targetSdkVersion="21" />
   
   <!-- Permissions for operation  -->
   <uses-permission android:name="android.permission.INTERNET" />
   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
   <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> 
   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
   <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>

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

               <category android:name="android.intent.category.LAUNCHER" />
           </intent-filter>
       </activity>
        <!-- Google API Key -->
       <meta-data
           android:name="com.google.android.maps.v2.API_KEY"
           android:value="AIzaSyDX06H1NUH4gcqcoqPgoxHpw_eEEqZzOPA" />
       <meta-data
           android:name="com.google.android.gms.version"
           android:value="@integer/google_play_services_version" />
   </application>

</manifest>

II-B-2. XML

Ensuite nous aurons besoin de déclarer le fichier XML que nous allons appeler activity_main.xml. Depuis ce fichier nous devrons ajouter le fragment qui inclura la classe com.google.android.gms.maps.SupportMapFragment qui permettra d'afficher la carte.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context="com.android2ee.tileprovider.MainActivity" >
   
   <fragment
       android:id="@+id/map"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       class="com.google.android.gms.maps.SupportMapFragment" />
</FrameLayout>

II-B-3. JAVA

II-B-3-a. Le fichier MainActivity

 
Sélectionnez
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.
/**
* Main Activity
*
*/
public class MainActivity extends ActionBarActivity implements LocationListener, OnCameraChangeListener {

// Declaration
private GoogleMap map;
private MyTileProvider tileProvider;
private int minZoom = -1;
private int maxZoom = -1;

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

   map = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
       .getMap();
  
   // Init Map
   if (map != null){
         // set the type to TYPE_NONE
       map.setMapType(GoogleMap.MAP_TYPE_NONE);
       // create the tileProvider
       tileProvider = new MyTileProvider(this, "test.mbtiles");
       // Add the Provider in Map
       map.addTileOverlay(new TileOverlayOptions().tileProvider(tileProvider));
       // center Map
       LatLngBounds bounds = tileProvider.getBounds();
       CameraUpdate upd = CameraUpdateFactory.newLatLngBounds(bounds,MyTileProvider.TILE_WIDTH , MyTileProvider.TILE_HEIGHT, 0);
       minZoom = tileProvider.getMinZoom();
       maxZoom = tileProvider.getMaxZoom();
       map.setOnCameraChangeListener(this);
       map.moveCamera(upd);
   }
   
}

@Override
public void onCameraChange(CameraPosition position) {
   if (minZoom != -1) {
   if (position.zoom < minZoom)
      map.animateCamera(CameraUpdateFactory.zoomTo(minZoom));
   }
   if (maxZoom != -1) {
      if (position.zoom > maxZoom)
         map.animateCamera(CameraUpdateFactory.zoomTo(maxZoom));
   }
}

@Override
protected void onDestroy() {
   super.onDestroy();
   //  we close the database
   if (tileProvider != null) {
      tileProvider.close();
   }
}

Pour expliquer les différentes étapes :

  • initialisation de la variable map en récupérant le fragment déclaré dans notre fichier XML
 
Sélectionnez
map = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
       .getMap();
  • configuration de la GoogleMap en déclarant son type de carte à MAP_TYPE_NONE
 
Sélectionnez
map.setMapType(GoogleMap.MAP_TYPE_NONE);
  • création de notre TileProvider en donnant le nom du MbTiles présent dans les assets.
 
Sélectionnez
1.
tileProvider = new MyTileProvider(this, "test.mbtiles");

Nous verrons par la suite que nous copierons la base de donnée présente dans les assets dans l'espace interne de l'application.

  • ajout de ce TileProvider dans la GoogleMap
 
Sélectionnez
map.addTileOverlay(new TileOverlayOptions().tileProvider(tileProvider));
  • positionnement de la Google Map en fonction des tuiles présentes dans le MbTiles
 
Sélectionnez
LatLngBounds bounds = tileProvider.getBounds();
CameraUpdate upd = CameraUpdateFactory.newLatLngBounds(bounds,MyTileProvider.TILE_WIDTH , MyTileProvider.TILE_HEIGHT, 0);
map.moveCamera(upd);
  • limitation des zooms en fonction des tuiles présentes dans le MbTiles
 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
@Override
protected void onCreate(Bundle savedInstanceState) {
   ...
   minZoom = tileProvider.getMinZoom()
   maxZoom = tileProvider.getMaxZoom();
   map.setOnCameraChangeListener(this);
   …
}

@Override
public void onCameraChange(CameraPosition position) {
   if (minZoom != -1) {
      if (position.zoom < minZoom) 
         map.animateCamera(CameraUpdateFactory.zoomTo(minZoom));
   }
   if (maxZoom != -1) {
      if (position.zoom > maxZoom)
         map.animateCamera(CameraUpdateFactory.zoomTo(maxZoom));
   }
}

II-B-3-b. Le fichier MyTileProvider

 
Sélectionnez
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.
/**
* Tile Provider
*
*/
public class MyTileProvider implements TileProvider, Closeable {

  // declaration
  public static final int TILE_WIDTH = 256;
  public static final int TILE_HEIGHT = 256;

  private Context mContext;
  private DatabaseHelper dbHelper;

  public MyTileProvider(Context context, String name) {
    super();
    mContext = context;
    // load bd
    openTiles(name);
  }

  /**
  * Load Db
  * @param name
  */
  private void openTiles(String name) {
    dbHelper = new DatabaseHelper(mContext, name);
    try {
      dbHelper.createDatabase();

      dbHelper.openDataBase();
   
    } catch (IOException e) {
      Log.e(getClass().getName(), e.getMessage());
    }
  }

  @Override
  public Tile getTile(int x, int y, int zoom) {
    Tile result = null;
    // convert column due to te mbtile
    int column = ((int) (Math.pow(2, zoom) - y) - 1);
    // call request in db
    Cursor data = dbHelper.getTile(x, column, zoom);
    if (data != null) {
      if (data.moveToFirst()) {
        // get the bitmap
        int clmnindex = data.getColumnIndex("tile_data");
        byte[] img = data.getBlob(clmnindex);
        result = new Tile(TILE_WIDTH, TILE_HEIGHT, img);
      }
      data.close();
    }
    if (result == null) {
      // load an empty image
      result = NO_TILE;
    }
    return result;
  }

  /**
  * Get Bounds of map present in db
  * @return
  */
  public LatLngBounds getBounds() {
    LatLngBounds result = null;
    Cursor data = dbHelper.getBoundsMap();
    if (data != null) {
       if (data.moveToFirst()) {
        int columnIndex = data.getColumnIndex("value");
        String value = data.getString(columnIndex);
        String[] values = value.split(",");
        LatLng southwest = new LatLng(Double.parseDouble(values[1]),         Double.parseDouble(values[0]));
        LatLng northeast = new LatLng(Double.parseDouble(values[3]),         Double.parseDouble(values[2]));
        result = new LatLngBounds(southwest, northeast);
      }
      data.close();
    }
    return result;
  }

  /**
  * Get Min Zoom present in db
  * @return
  */
  public int getMinZoom() {
    Integer result = -1;
    Cursor data = dbHelper.getMinZoom();
    if (data != null) {
      if (data.moveToFirst()) {
        int columnIndex = data.getColumnIndex("value");
        result = data.getInt(columnIndex);

      }
      data.close();
    }
    return result;
  }

  /**
  * Get Max Zoom present in db
  * @return
  */
  public int getMaxZoom() {
    Integer result = -1;
    Cursor data = dbHelper.getMaxZoom();
    if (data != null) {
      if (data.moveToFirst()) {
        int columnIndex = data.getColumnIndex("value");
        result = data.getInt(columnIndex);
      }
      data.close();
    }
    return result;
  }

  @Override
  public void close() {
    if (dbHelper != null) {
      dbHelper.close();
    }
  }

  @Override
  protected void finalize() throws Throwable {
    super.finalize();
    // try it.
    close();
  }

}

Pour expliquer les étapes :

  • ouverture du MbTiles
 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
/**
* Load Db
* @param name
*/
private void openTiles(String name) {
  dbHelper = new DatabaseHelper(mContext, name);
  try {
    dbHelper.createDatabase();
    dbHelper.openDataBase();
  } catch (IOException e) {
    Log.e(getClass().getName(), e.getMessage());
  }
}
  • récupération des tuiles
 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
@Override
public Tile getTile(int x, int y, int zoom) {
  Tile result = null;
  // convert column due to the mbtile
  int column = ((int) (Math.pow(2, zoom) - y) - 1);
  // call request in db
  Cursor data = dbHelper.getTile(x, column, zoom);
  if (data != null) {
    if (data.moveToFirst()) {
      // get the bitmap
      int clmnindex = data.getColumnIndex("tile_data");
      byte[] img = data.getBlob(clmnindex);
      result = new Tile(TILE_WIDTH, TILE_HEIGHT, img);
    }
    data.close();
  }
  if (result == null) {
    // load an empty image
    result = NO_TILE;
  }
  return result;
}

NO_TILE permet de préciser que vous n'avez pas de carte associée à la demande. Cela permettra à GoogleMap de ne pas vous redemander la carte si cette tuile est ré-affichée.

Pour associer les valeurs x et y demandées par GoogleMap avec les colonnes et lignes présentes dans le MbTiles il vous faudra appliquer cette formule de conversion int column = ((int) (Math.pow(2, zoom) - y) - 1); !

  • création des requêtes pour accéder au MbTiles

Les fonctions associées au MbTiles comme getMaxZoom, getMinZoom, getBounds, etc...

  • fermeture du MbTiles
 
Sélectionnez
1.
2.
3.
4.
5.
6.
@Override
public void close() {
  if (dbHelper != null) {
    dbHelper.close();
  }
}

II-B-3-c. Le fichier DataBaseHelper

 
Sélectionnez
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.
/**
* The DataBase Helper 
*
*/
public class DatabaseHelper extends SQLiteOpenHelper {

   // TODO change it
    private static final String DB_SUBPATH = "/databases/";
   private String dbPath;
    private String dbName;
    private static final Integer DB_VERSION = 1;
    private SQLiteDatabase myDataBase;
   private final Context myContext;

   public DatabaseHelper (Context context, String name) {
        super(context, name, null, DB_VERSION);
        this.myContext = context;
      this.dbName = name;
       this.dbPath = context.getFilesDir().getAbsolutePath() + DB_SUBPATH;
        File file = new File(dbPath);
        if (!file.exists()) {
        file.mkdirs();
        }
   }

   /**
    * Create database
    * @param name
    * @throws IOException
    */
   public void createDatabase() throws IOException {
       boolean isExist = isDatabaseExist();

       if (!isExist) {
           this.getReadableDatabase();
           try {
               copyDataBase();
            } catch (IOException e) {
            throw new Error("Error copying database");
            }
       }
   }

   /**
    * CopyDataBase from Asset to the internal storage
    * @param name
    * @throws IOException
    */
   private void copyDataBase() throws IOException {

       // Open your local db as the input stream
        InputStream myInput = myContext.getAssets().open(dbName);
       
        // Path to the just created empty db
        String outFileName = dbPath + dbName;

       // Open the empty db as the output stream
        OutputStream myOutput = new FileOutputStream(outFileName);

       // transfer bytes from the inputfile to the outputfile
        byte[] buffer = new byte[1024];
        int length;
       while ((length = myInput.read(buffer)) > 0) {
         myOutput.write(buffer, 0, length);
        }

       // Close the streams
        myOutput.flush();
        myOutput.close();
       myInput.close();
   }

   /**
    * Test if the database already exist
    * @return
    */
   private boolean isDatabaseExist() {
      SQLiteDatabase control = null;

      try {
          String myPath = dbPath + dbName;
            control = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
        } catch (SQLiteException e) {
        control = null;
       }

       if (control != null) {
        control.close();
       }
        return control != null ? true : false;
   }

   /**
    * Open DataBase
    * @throws SQLException
    */
   public void openDataBase() throws SQLException {

        // Open the database
       String myPath = dbPath + dbName;
        myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READWRITE);

   }

   /**
    * Get Tile in Database
    * @param row : the row 
    * @param column : the column,
    * @param zoom : the zoom
    * @return the byte[]
    */
   public Cursor getTile(int row, int column, int zoom) {

        return myDataBase.query("tiles", new String[] {"tile_data"}, "tile_row = ? AND tile_column = ? AND zoom_level = ?", 
        new String[]{Integer.toString(column), Integer.toString(row), Integer.toString(zoom)}, null, null, null);
   }
   
   /**
    * Get Bounds present in db
    * @return
    */
   public Cursor getBoundsMap() {
        return myDataBase.query("metadata", new String[] {"value"}, "name like \"bounds\"", null, null, null, null);
   }
   
   /**
    * get Min Zoom present in db
    * @return
    */
   public Cursor getMinZoom() {
        return myDataBase.query("metadata", new String[] {"value"}, "name like \"minzoom\"", null, null, null, null);
   }
   
   /**
    * get Max Zoom present in db
    * @return
    */
   public Cursor getMaxZoom() {
        return myDataBase.query("metadata", new String[] {"value"}, "name like \"maxzoom\"", null, null, null, null);
   }

   @Override
   public synchronized void close() {
       if (myDataBase != null)
            myDataBase.close();
        super.close();
   }

   @Override
   public void onCreate(SQLiteDatabase db) {
   }

   @Override
   public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
   }
}

Cette classe reste une classe simple implémentant une base de donnée existante, et permettant d'effectuer des requêtes SQLite sur celle-ci.

Nous trouverons alors les requêtes sur la table metadata comme la récupération des contours, du zoom maximum, du zoom minimum , etc…, ainsi que la requêtes sur la récupération des tuiles sur la table tile_data.

La base de donnée présente dans les assets sera copiée en interne via cette fonction copyDataBase(); De ce fait cet exemple ne marche seulement qu'avec une base de donnée déjà existante depuis le dossier assets du projet, auquel nous aurons passé le nom du fichier lors de la création de cette classe.

II-B-3-d. Le rendu

Voici le résultat :

   
Image non disponible

II-C. GroundOverlays

Le GroundOverlays permet de modifier des aires très légère sur la carte de Google Maps normale ou personnalisée. Cela vous permettra de pouvoir ajouter une image de fond simple sur une petite surface pour avoir un rendu personnalisé. Attention seule une image de fond peut être ajoutée sur l'ensemble de la carte. Son utilisation est très simple. Il n'y a pas de gestion de ressource comme pour les TileOverlay, seule une initialisation avec les données  de l'image personnalisée à afficher est nécessaire.

 
Sélectionnez
1.
2.
3.
4.
5.
LatLng TOULOUSE = new LatLng(43.614086, 1.428697);
GroundOverlayOptions groundMap = new GroundOverlayOptions()
       .image(BitmapDescriptorFactory.fromResource(R.drawable.background))
       .position(TOULOUSE, 8600f, 6500f);
map.addGroundOverlay(groundMap);

Ne pas oubliez de passer la carte en MAP_TYPE_NONE si vous êtes sur une carte personnalisée ou bien MAP_TYPE_NORMAL, si vous êtes sur une carte de google.

image : L'image qui se positionnera sur les tuiles de la carte.

position : La position de l'image , LatLng (latitude, longitude), largeur, hauteur.

Le GroundOverlay positionne une image sur la carte définie par sa position en Latitude, longitude ainsi que la hauteur et largeur, que nous avons défini lors de la construction de cet objet.

II-C-1. Le rendu

center ce que nous obtenons :

Image non disponible

III. Conclusion

La possibilité de pouvoir insérer nos propres cartes ou une seule image, permet de pouvoir gérer nos propres images de carte de fond (possibilité d'afficher/cacher des informations spécifiques sur la carte) ainsi que l'affichage des données en mode hors-ligne . Tout en ayant les optimisations apportées par Google Map sur l'affichage des tuiles sous Android. L'affichage de la carte reste très fluide.

Sa mise en place est très simple, juste la gestion de la taille des données reste problématique sur la possibilité de stocker un ensemble important sur l'appareil, il vous faudra effectuer des choix sur le zoom maximum et minimum ainsi que sur la zone de couverture de votre carte.

Le projet sous github : https://github.com/ffournier/TileProvider/

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2015 Florian. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.