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 :
- Depuis la page Services, vérifiez que vous avez sélectionné "Google Maps Android API v2" ;
- Ensuite depuis le menu de navigation, cliquez sur API Access ;
- Vous trouverez alors un bouton «Create New Android Key», cliquez dessus ;
- 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 ;
- Vous obtiendrez ensuite une clé générée que vous devrez sauvegarder afin 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 :
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 :
- La clé de l'API que vous avez générée préalablement ;
- De spécifier quelle version de la bibliothèque Google Play Services vous utilisez.
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 compatible 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▲
Il ne vous reste plus qu'à récupérer ce composant graphique depuis votre code JAVA en vue de le configurer pour diverses utilisations. 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. Autres informations▲
Attention, il vous sera aussi utile de tester, si le Service de Google Play est disponible :
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 Google Play 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 ait 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ées SQLite contenant les tuiles de la carte que nous voulons afficher. Cette base de données 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ères qui détaille les données présente de la carte ;
-
une table tiles
- zoom_level : le niveau de zoom demandé,
- 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 ce sont 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 fichiers 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 d'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 TileProviderou la UrlTileProvider.
La TileProvider fournit 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-ci sera défini par défaut à 0 jusqu'à n, il faut voir l'ensemble des tuiles comme une représentation de couches empilées où 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émarrent à 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. Cette 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 feront 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écessaires à Google Maps, ainsi que les métadonnées.
Pensez également à inclure la bibliothèque google play service à votre projet !
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.
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▲
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.
/**
* 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
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
tileProvider =
new
MyTileProvider
(
this
, "test.mbtiles"
);
Nous verrons par la suite que nous copierons la base de données 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
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▲
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
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
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
2.
3.
4.
5.
6.
@Override
public
void
close
(
) {
if
(
dbHelper !=
null
) {
dbHelper.close
(
);
}
}
II-B-3-c. Le fichier DataBaseHelper▲
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.
/**
* 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
test
*/
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ées 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ête sur la récupération des tuiles sur la table tile_data.
La base de données 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ées 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ères sur la carte de Google Maps normale ou personnalisée. Cela vous permettra d'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.
2.
3.
4.
5.
LatLng TOULOUSE =
new
LatLng
(
43.614086
, 1.428697
);
GroundOverlayOptions groundMap =
new
GroundOverlayOptions
(
)
.image
(
BitmapDescriptorFactory.fromResource
(
R.drawable.background))
.position
(
TOULOUSE, 8600
f, 6500
f);
map.addGroundOverlay
(
groundMap);
Ne pas oublier 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éfinies lors de la construction de cet objet.
II-C-1. Le rendu▲
Voici ce que nous obtenons :
III. Conclusion▲
La possibilité d'insérer nos propres cartes ou une seule image permet de gérer nos propres images de cartes 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/
IV. Remerciements Developpez▲
Mes remerciements à Claude Leloup pour sa relecture orthographique et également à Mickael Baron pour sa relecture technique
N'hésitez pas à commenter cet article ! 2 commentaires