D'autres sources de données cartographiques

Il existe de nombreuses sources de cartes de types différents accessibles sur Internet. J'avoue que je ne me suis pas penché sur l'aspect licence de ces cartes et je vous invite donc à beaucoup de précaution si vous souhaitez les utiliser. Je vous donnerai une liste assez importante de toutes les sources que j'ai pu trouver en fin d'article. Pour l'instant, nous allons utiliser la source suivante :

http://www.maps-for-free.com/layer/relief/z{z}/row{y}/{z}_{x}-{y}.jpg

Comme vous avez pu le remarquer, il y a, dans cette URL, trois champs qui devront être paramétrés pour que le serveur nous renvoie une image composant la carte :

  • x la position horizontale de l'élément,
  • y sa position vertical et
  • z pour le niveau de zoom.

Ainsi, par exemple, pour x = 31, y = 22 et z = 6, nous obtenons une vue du relief de la cote ouest de la France. Vous pouvez essayer de faire varier ces paramètres pour obtenir d'autres images composant la carte que nous souhaitons afficher.

En plus de l'URL et de ses paramètres, nous devons également connaître la dimension des images fournies et les niveaux de zoom minimum et maximum disponibles. Pour la source que nous avons choisi, ces informations sont les suivantes :

  • dimensions des images : 256 x 256 pixels,
  • zoom min : 0 et
  • zoom max : 11.

Pour la création de votre type de carte personnalisé, vous devrez indiquer si les images sont au format PNG. Pour la source de cette exemple, nous n'avons que des images au format JPEG.

Enfin, petite parenthèse, sachez que chaque image qui compose la carte est couramment appelée tile qui est parfois traduit en français par tuile.

Création d'un type de carte personnalisé

Notre carte étant basée sur des images, nous allons donc créer un objet de type ImageMapType. Mais avant tout, nous devons définir les paramètres de cet objet. Pour cela nous allons simplement créer un nouvel objet statique comme suit :

var myMapTypeOptions = {
 
  // Fonction retournant l'URL de l'image en fonction des coordonnées
  // X et Y et du niveau de zoom
  getTileUrl: function(coord, zoom) {
      return "http://www.maps-for-free.com/layer/relief/z" + 
          zoom + "/row" + coord.y + "/" + zoom + "_" + coord.x +
          "-" + coord.y + ".jpg";
  },
 
  // Nous definissons les dimensions des images (tiles)
  tileSize: new google.maps.Size(256, 256),
 
  // Le niveau de zoom max
  maxZoom: 11,
 
  // Minimum
  minZoom: 0,
 
  // Et enfin un nom "user friendly" pour le type de carte
  name: "Relief"
};

Puis nous pouvons enfin créer l'objet de type ImageMapType de la façon suivante :

var myMapType = new google.maps.ImageMapType( myMapTypeOptions );

Affichage de la carte

Nous avons créé notre nouveau type de carte, il ne nous reste donc plus qu'à l'afficher dans une carte classique. Cette partie reprendra des éléments de mon billet sur la gestion des différents types de cartes mais nous allons devoir aller un peu plus loin.

Pour des raison purement pratiques, nous allons d'abord définir un identifiant pour notre nouveau type de carte :

// Une simple chaîne de caractères dans une variable
var myMapTypeId = 'relief';

A partir de là, nous pouvons créer notre carte comme suit :

// Point sur lequel est centré la carte
var myLatlng = new google.maps.LatLng(0, 0);
 
// Options de la carte
var myOptions = {
	zoom: 3,
	center: myLatlng,
        // Nous indiquons ici le type de carte à afficher par défaut :
	mapTypeId: myMapTypeId , // ID définit plus haut
        // Personnalisation du sélecteur
        mapTypeControlOptions : {
          // Liste des types autorisés
          mapTypeIds : [ myMapTypeId  ],
          // Apparence du sélecteur
          style : google.maps.MapTypeControlStyle.DEFAULT
        }
};
 
// Création de la carte
var myMap = new google.maps.Map(
	document.getElementById('map'),
	myOptions
);

Enfin, pour que notre nouveau type fonctionne, nous devons l'ajouter à notre carte avec le code suivant :

myMap.mapTypes.set( myMapTypeId , myMapType );

Vous pouvez observer le résultat sur la page d'exemple. Vous pouvez aussi regarder la source de la page pour consulter le code complet.

Le cas des cartes inversées (carte de la Lune)

Certaines sources de données utilisent des coordonnées de carte verticales (y) inversées. Je n'ai pas encore compris pourquoi, mais si quelqu'un veut apporter son explication, qu'il n'hésite pas à commenter ce billet. Pour illustrer cette particularité, je vous propose d'exploiter une nouvelle source de données : les cartes Google de la Lune ! L'URL pour accéder aux images de la carte est ainsi formée :

http://www.google.com/moon,http://mw1.google.com/mw-planetary/lunar/lunarmaps_v1/clem_bw/{z}/{x}/{-y}.jpg

La notation -y est là pour indiquer que la coordonnées y est inversée. Pour afficher cette carte, nous pourrions donc être tenté d'utiliser la définition suivante :

var myMapTypeOptions = {
 
  // Fonction retournant l'URL de l'image en fonction des coordonnées
  getTileUrl: function(coord, zoom) {
      return "http://mw1.google.com/mw-planetary/lunar/lunarmaps_v1/clem_bw/" +
            zoom + "/" + coord.x + "/" + coord.y + ".jpg";
  },
 
  // Les autres paramètres ne changent pas
  tileSize: new google.maps.Size(256, 256),
  maxZoom: 11,
  minZoom: 0,
 
  // Sauf peut être le nom
  name: "Lune"
};

Pour que vous compreniez bien le problème, j'ai réalisé un exemple de ce que donnerait ce code. Vous l'aurez compris, les images ne sont pas affichées dans le bon ordre sur l'axe vertical. Pour résoudre ce problème, nous allons utiliser un petit calcul qui nous permettra de convertir y en son opposé :

var minY = Math.pow(2, zoom) - coord.y - 1;

Nous utiliserons donc minY à la place de coord.y. Revenons à la fonction qui retourne l'URL des images. Nous la modifions pour obtenir le résultat suivant :

getTileUrl: function(coord, zoom) {
      var minY = Math.pow(2, zoom) - coord.y - 1;
      return "http://mw1.google.com/mw-planetary/lunar/lunarmaps_v1/clem_bw/" +
            zoom + "/" + coord.x + "/" + minY  + ".jpg";
  }

Vous pourrez constater que le résultat est bien plus probant.

Cette partie sur les cartes dont l'axe vertical est inversé est un peu floue pour moi. Malgré mes nombreuses recherches, je n'ai pas trouvé pourquoi cette inversion et comment le calcul que je vous ai donné résout le problème. Je vous livre donc cette solution sans plus d'explications mais si quelqu'un peut m'expliquer tout ça, qu'il n'hésite pas !

Répétition horizontale de la carte

Si vous souhaitez reproduire le fonctionnement normal des cartes Google, vous souhaiterez que la carte se répète horizontalement. Pour cela, nous allons utiliser une nouvelle fonction qui nous permettra de changer les coordonnées des images à afficher à la demande :

// Normalizes the coords that tiles repeat across the x axis (horizontally)
// like the standard Google map tiles.
function getNormalizedCoord(coord, zoom) {
  var y = coord.y;
  var x = coord.x;
 
  // tile range in one direction range is dependent on zoom level
  // 0 = 1 tile, 1 = 2 tiles, 2 = 4 tiles, 3 = 8 tiles, etc
  var tileRange = 1 << zoom;
 
  // don't repeat across y-axis (vertically)
  if (y < 0 || y >= tileRange) {
    return null;
  }
 
  // repeat across x-axis
  if (x < 0 || x >= tileRange) {
    x = (x % tileRange + tileRange) % tileRange;
  }
 
  return {
    x: x,
    y: y
  };
}

Encore une fois, les aspects mathématiques m'échappent. Je vous livre donc de nouveau ce code sans plus d'explications. Je l'ai piqué sur la documentation officielle de l'API et elle fonctionne parfaitement. Nous allons donc l'intégrer dans la fonction qui renvoie les URL de nos images. Reprenons donc notre carte de relief et modifions le code pour obtenir la fonction suivante :

// Fonction retournant l'URL de l'image en fonction des coordonnées
  // X et Y et du niveau de zoom
  getTileUrl: function(coord, zoom) {
 
      // on ajoute la ligne suivante pour transformer les coordonnées
      coord = getNormalizedCoord(coord, zoom);
 
      // si la fonction renvoie null, alors il n'y a pas d'image à afficher
      if( ! coord ) {
        return null;
      }
 
      // on retourne enfin l'URL
      return "http://www.maps-for-free.com/layer/relief/z" + 
          zoom + "/row" + coord.y + "/" + zoom + "_" + coord.x +
          "-" + coord.y + ".jpg";
  }

Je vous laisse observer le résultat. Personnellement, je trouve que cette carte de reliefs est bien plus parlante que celle fournie par Google et nous avons vu que moyennant un peu de code il est tout à fait possible de l'afficher de la même manière que la carte originale. Désormais, vous pouvez afficher n'importe quelle carte !

Plein d'autres cartes

Je vous invite a visiter la page listant les sources de cartes que j'ai trouvé. Vous pouvez également consulter cette exemple de carte Google Map qui reprends plusieurs sources de données. Dans la source de la page, vous verrez un exemple de code organisé pour gérer assez simplement différents types de cartes.

Conclusion

Le fait de pouvoir charger n'importe quelle carte dans les Google Map offre de nouvelles possibilités ! Naturellement, créer une carte est un travail long et compliqué... mais ce système pourrait tout a fait être utilisé pour un jeu en 2D par exemple ! Nous verrons, dans un prochain billet, comment personnaliser les contrôles disponibles sur la carte (vous pourrez alors virer le logo Streetview de vos cartes de la Lune ;-)).

<< Sommaire des articles Google Map API V3