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

Auteur

Florian

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 ).

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

- Auteur : Florian

- Parution : 7 février 2015

- Licence : 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.

Sommaire

- I. Google Map v2 pour Android

- I-A. Certificat

- I-A-1. En debug

- I-A-2. Release

- I-B. Installation

- I-C. Déploiement

- I-C-1. Le fichier Manifest

- I-C-2. XML

- I-C-3. JAVA

- I-C-4. Autre informations

- I-D. Support des cartes à intégrer

- I-D-1. MbTiles

- I-D-2. Vector Tiles

- I-D-3. Fichier

- I-D-4. Autres

- II. Fonctionnement

- II-A. TileOverlays

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

- II-B-1. Le fichier Manifest

- II-B-2. XML

- II-B-3. JAVA

- II-B-3-a. Le fichier MainActivity

- II-B-3-b. Le fichier MyTileProvider

- II-B-3-c. Le fichier DataBaseHelper

- II-B-3-d. Le rendu

- II-C. GroundOverlays

- II-C-1. Le rendu

- III. Conclusion

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

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

Windows

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

I-A-2 - Release

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 :

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

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

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

4. <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.

1. <!-- Google API Key -->

2. <meta-data

3. android:name="com.google.android.maps.v2.API_KEY"

4. android:value="your_key" />

5. <meta-data

6. android:name="com.google.android.gms.version"

7. 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 :

<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 :

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

De même pour une MapFragment :

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 :

1. // Getting Google Play availability status

2. int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(getBaseContext());

3. // Showing status

4. if(status!=ConnectionResult.SUCCESS){ // Google Play Services are not available

5. Dialog dialog = GooglePlayServicesUtil.getErrorDialog(status, this, requestCode);

6. dialog.show();

7. }

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 :

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 :

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 :

GoogleMap map; // ... get a map.
TileProvider tileProvider; // ... create a tile provider.
TileOverlay tileOverlay = map.addTileOverlay(
new TileOverlayOptions().tileProvider(tileProvider));

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 :

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 !

1. <?xml version="1.0" encoding="utf-8"?>

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

3. package="com.android2ee.tileprovider"

4. android:versionCode="1"

5. android:versionName="1.0" >

7. <uses-sdk

8. android:minSdkVersion="8"

9. android:targetSdkVersion="21" />

11. <!-- Permissions for operation  -->

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

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

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

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

16. <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>

18. <application

19. android:allowBackup="true"

20. android:icon="@drawable/ic_launcher"

21. android:label="@string/app_name"

22. android:theme="@style/AppTheme" >

23. <activity

24. android:name=".MainActivity"

25. android:label="@string/app_name" >

26. <intent-filter>

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

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

30. </intent-filter>

31. </activity>

32. <!-- Google API Key -->

33. <meta-data

34. android:name="com.google.android.maps.v2.API_KEY"

35. android:value="AIzaSyDX06H1NUH4gcqcoqPgoxHpw_eEEqZzOPA" />

36. <meta-data

37. android:name="com.google.android.gms.version"

38. android:value="@integer/google_play_services_version" />

39. </application>

41. </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.

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

2. xmlns:tools="http://schemas.android.com/tools"

3. android:layout_width="match_parent"

4. android:layout_height="match_parent"

5. tools:context="com.android2ee.tileprovider.MainActivity" >

7. <fragment

8. android:id="@+id/map"

9. android:layout_width="match_parent"

10. android:layout_height="match_parent"

11. class="com.google.android.gms.maps.SupportMapFragment" />

12. </FrameLayout>

II-B-3 - JAVA

II-B-3-a - Le fichier MainActivity

1. /**

2. * Main Activity

3. *

4. */

5. public class MainActivity extends ActionBarActivity implements LocationListener, OnCameraChangeListener {

7. // Declaration

8. private GoogleMap map;

9. private MyTileProvider tileProvider;

10. private int minZoom = -1;

11. private int maxZoom = -1;

13. @Override

14. protected void onCreate(Bundle savedInstanceState) {

15. @Override

16. protected void onCreate(Bundle savedInstanceState) {

17. super.onCreate(savedInstanceState);

18. setContentView(R.layout.activity_main);

20. map = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))

21. .getMap();

23. // Init Map

24. if (map != null){

25. // set the type to TYPE_NONE

26. map.setMapType(GoogleMap.MAP_TYPE_NONE);

27. // create the tileProvider

28. tileProvider = new MyTileProvider(this, "test.mbtiles");

29. // Add the Provider in Map

30. map.addTileOverlay(new TileOverlayOptions().tileProvider(tileProvider));

31. // center Map

32. LatLngBounds bounds = tileProvider.getBounds();

33. CameraUpdate upd = CameraUpdateFactory.newLatLngBounds(bounds,MyTileProvider.TILE_WIDTH , MyTileProvider.TILE_HEIGHT, 0);

34. minZoom = tileProvider.getMinZoom();

35. maxZoom = tileProvider.getMaxZoom();

36. map.setOnCameraChangeListener(this);

37. map.moveCamera(upd);

38. }

40. }

42. @Override

43. public void onCameraChange(CameraPosition position) {

44. if (minZoom != -1) {

45. if (position.zoom < minZoom)

46. map.animateCamera(CameraUpdateFactory.zoomTo(minZoom));

47. }

48. if (maxZoom != -1) {

49. if (position.zoom > maxZoom)

50. map.animateCamera(CameraUpdateFactory.zoomTo(maxZoom));

51. }

52. }

54. @Override

55. protected void onDestroy() {

56. super.onDestroy();

57. //  we close the database

58. if (tileProvider != null) {

59. tileProvider.close();

60. }

61. }

Pour expliquer les différentes étapes :

- initialisation de la variable map en récupérant le fragment déclaré dans notre fichier XML

map = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
.getMap();

- configuration de la GoogleMap en déclarant son type de carte à MAP_TYPE_NONE

map.setMapType(GoogleMap.MAP_TYPE_NONE);

- création de notre TileProvider en donnant le nom du MbTiles présent dans les assets.

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

map.addTileOverlay(new TileOverlayOptions().tileProvider(tileProvider));

- positionnement de la Google Map en fonction des tuiles présentes dans le MbTiles

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

1. @Override

2. protected void onCreate(Bundle savedInstanceState) {

3. ...

4. minZoom = tileProvider.getMinZoom()

5. maxZoom = tileProvider.getMaxZoom();

6. map.setOnCameraChangeListener(this);

7. …

8. }

10. @Override

11. public void onCameraChange(CameraPosition position) {

12. if (minZoom != -1) {

13. if (position.zoom < minZoom)

14. map.animateCamera(CameraUpdateFactory.zoomTo(minZoom));

15. }

16. if (maxZoom != -1) {

17. if (position.zoom > maxZoom)

18. map.animateCamera(CameraUpdateFactory.zoomTo(maxZoom));

19. }

20. }

II-B-3-b - Le fichier MyTileProvider

1. /**

2. * Tile Provider

3. *

4. */

5. public class MyTileProvider implements TileProvider, Closeable {

7. // declaration

8. public static final int TILE_WIDTH = 256;

9. public static final int TILE_HEIGHT = 256;

11. private Context mContext;

12. private DatabaseHelper dbHelper;

14. public MyTileProvider(Context context, String name) {

15. super();

16. mContext = context;

17. // load bd

18. openTiles(name);

19. }

21. /**

22. * Load Db

23. * @param name

24. */

25. private void openTiles(String name) {

26. dbHelper = new DatabaseHelper(mContext, name);

27. try {

28. dbHelper.createDatabase();

30. dbHelper.openDataBase();

32. } catch (IOException e) {

33. Log.e(getClass().getName(), e.getMessage());

34. }

35. }

37. @Override

38. public Tile getTile(int x, int y, int zoom) {

39. Tile result = null;

40. // convert column due to te mbtile

41. int column = ((int) (Math.pow(2, zoom) - y) - 1);

42. // call request in db

43. Cursor data = dbHelper.getTile(x, column, zoom);

44. if (data != null) {

45. if (data.moveToFirst()) {

46. // get the bitmap

47. int clmnindex = data.getColumnIndex("tile_data");

48. byte[] img = data.getBlob(clmnindex);

49. result = new Tile(TILE_WIDTH, TILE_HEIGHT, img);

50. }

51. data.close();

52. }

53. if (result == null) {

54. // load an empty image

55. result = NO_TILE;

56. }

57. return result;

58. }

60. /**

61. * Get Bounds of map present in db

62. * @return

63. */

64. public LatLngBounds getBounds() {

65. LatLngBounds result = null;

66. Cursor data = dbHelper.getBoundsMap();

67. if (data != null) {

68. if (data.moveToFirst()) {

69. int columnIndex = data.getColumnIndex("value");

70. String value = data.getString(columnIndex);

71. String[] values = value.split(",");

72. LatLng southwest = new LatLng(Double.parseDouble(values[1]),         Double.parseDouble(values[0]));

73. LatLng northeast = new LatLng(Double.parseDouble(values[3]),         Double.parseDouble(values[2]));

74. result = new LatLngBounds(southwest, northeast);

75. }

76. data.close();

77. }

78. return result;

79. }

81. /**

82. * Get Min Zoom present in db

83. * @return

84. */

85. public int getMinZoom() {

86. Integer result = -1;

87. Cursor data = dbHelper.getMinZoom();

88. if (data != null) {

89. if (data.moveToFirst()) {

90. int columnIndex = data.getColumnIndex("value");

91. result = data.getInt(columnIndex);

93. }

94. data.close();

95. }

96. return result;

97. }

99. /**

100. * Get Max Zoom present in db

101. * @return

102. */

103. public int getMaxZoom() {

104. Integer result = -1;

105. Cursor data = dbHelper.getMaxZoom();

106. if (data != null) {

107. if (data.moveToFirst()) {

108. int columnIndex = data.getColumnIndex("value");

109. result = data.getInt(columnIndex);

110. }

111. data.close();

112. }

113. return result;

114. }

116. @Override

117. public void close() {

118. if (dbHelper != null) {

119. dbHelper.close();

120. }

121. }

123. @Override

124. protected void finalize() throws Throwable {

125. super.finalize();

126. // try it.

127. close();

128. }

130. }

Pour expliquer les étapes :

- ouverture du MbTiles

1. /**

2. * Load Db

3. * @param name

4. */

5. private void openTiles(String name) {

6. dbHelper = new DatabaseHelper(mContext, name);

7. try {

8. dbHelper.createDatabase();

9. dbHelper.openDataBase();

10. } catch (IOException e) {

11. Log.e(getClass().getName(), e.getMessage());

12. }

13. }

- récupération des tuiles

1. @Override

2. public Tile getTile(int x, int y, int zoom) {

3. Tile result = null;

4. // convert column due to the mbtile

5. int column = ((int) (Math.pow(2, zoom) - y) - 1);

6. // call request in db

7. Cursor data = dbHelper.getTile(x, column, zoom);

8. if (data != null) {

9. if (data.moveToFirst()) {

10. // get the bitmap

11. int clmnindex = data.getColumnIndex("tile_data");

12. byte[] img = data.getBlob(clmnindex);

13. result = new Tile(TILE_WIDTH, TILE_HEIGHT, img);

14. }

15. data.close();

16. }

17. if (result == null) {

18. // load an empty image

19. result = NO_TILE;

20. }

21. return result;

22. }

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

1. @Override

2. public void close() {

3. if (dbHelper != null) {

4. dbHelper.close();

5. }

6. }

II-B-3-c - Le fichier DataBaseHelper

1. /**

2. * The DataBase Helper

3. *

4. */

5. public class DatabaseHelper extends SQLiteOpenHelper {

7. // TODO change it

8. private static final String DB_SUBPATH = "/databases/";

9. private String dbPath;

10. private String dbName;

11. private static final Integer DB_VERSION = 1;

12. private SQLiteDatabase myDataBase;

13. private final Context myContext;

15. public DatabaseHelper (Context context, String name) {

16. super(context, name, null, DB_VERSION);

17. this.myContext = context;

18. this.dbName = name;

19. this.dbPath = context.getFilesDir().getAbsolutePath() + DB_SUBPATH;

20. File file = new File(dbPath);

21. if (!file.exists()) {

22. file.mkdirs();

23. }

24. }

26. /**

27. * Create database

28. * @param name

29. * @throws IOException

30. */

31. public void createDatabase() throws IOException {

32. boolean isExist = isDatabaseExist();

34. if (!isExist) {

35. this.getReadableDatabase();

36. try {

37. copyDataBase();

38. } catch (IOException e) {

39. throw new Error("Error copying database");

40. }

41. }

42. }

44. /**

45. * CopyDataBase from Asset to the internal storage

46. * @param name

47. * @throws IOException

48. */

49. private void copyDataBase() throws IOException {

51. // Open your local db as the input stream

52. InputStream myInput = myContext.getAssets().open(dbName);

54. // Path to the just created empty db

55. String outFileName = dbPath + dbName;

57. // Open the empty db as the output stream

58. OutputStream myOutput = new FileOutputStream(outFileName);

60. // transfer bytes from the inputfile to the outputfile

61. byte[] buffer = new byte[1024];

62. int length;

63. while ((length = myInput.read(buffer)) > 0) {

64. myOutput.write(buffer, 0, length);

65. }

67. // Close the streams

68. myOutput.flush();

69. myOutput.close();

70. myInput.close();

71. }

73. /**

74. * Test if the database already exist

75. * @return

76. */

77. private boolean isDatabaseExist() {

78. SQLiteDatabase control = null;

80. try {

81. String myPath = dbPath + dbName;

82. control = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);

83. } catch (SQLiteException e) {

84. control = null;

85. }

87. if (control != null) {

88. control.close();

89. }

90. return control != null ? true : false;

91. }

93. /**

94. * Open DataBase

95. * @throws SQLException

96. */

97. public void openDataBase() throws SQLException {

99. // Open the database

100. String myPath = dbPath + dbName;

101. myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READWRITE);

103. }

105. /**

106. * Get Tile in Database

107. * @param row : the row

108. * @param column : the column,

109. * @param zoom : the zoom

110. * @return the byte[]

111. */

112. public Cursor getTile(int row, int column, int zoom) {

114. return myDataBase.query("tiles", new String[] {"tile_data"}, "tile_row = ? AND tile_column = ? AND zoom_level = ?",

115. new String[]{Integer.toString(column), Integer.toString(row), Integer.toString(zoom)}, null, null, null);

116. }

118. /**

119. * Get Bounds present in db

120. * @return

121. */

122. public Cursor getBoundsMap() {

123. return myDataBase.query("metadata", new String[] {"value"}, "name like \"bounds\"", null, null, null, null);

124. }

126. /**

127. * get Min Zoom present in db

128. * @return

129. */

130. public Cursor getMinZoom() {

131. return myDataBase.query("metadata", new String[] {"value"}, "name like \"minzoom\"", null, null, null, null);

132. }

134. /**

135. * get Max Zoom present in db

136. * @return

137. */

138. public Cursor getMaxZoom() {

139. return myDataBase.query("metadata", new String[] {"value"}, "name like \"maxzoom\"", null, null, null, null);

140. }

142. @Override

143. public synchronized void close() {

144. if (myDataBase != null)

145. myDataBase.close();

146. super.close();

147. }

149. @Override

150. public void onCreate(SQLiteDatabase db) {

151. }

153. @Override

154. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

155. }

156. }

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 :

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.

1. LatLng TOULOUSE = new LatLng(43.614086, 1.428697);

2. GroundOverlayOptions groundMap = new GroundOverlayOptions()

3. .image(BitmapDescriptorFactory.fromResource(R.drawable.background))

4. .position(TOULOUSE, 8600f, 6500f);

5. 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 :

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/