oogleMap et son frère GoogleEarth ont un succès considérable. Il n'y a pas un Internaute qui n'ait pas déjà joué à repérer sa maison, son lieu de travail ou sa location de vacances. Il est vrai que même pour ceux qui ne savent pas lire une carte, disposer d'une photo satellite, zoomer et se déplacer c'est vraiment magique.
Mais Google Map n'est pas seulement ça, c'est aussi l'une des applications les plus représentative de la mouvance "Web 2.0" dont nous entendons parler depuis des mois. Car avec Google Map les développeurs peuvent construire une "mashup-application": un site web qui intégre directement les fonctionnalités d'un autre site c'est à dire ici, un site qui intègrer la cartographie offerte par Google Map. Et c'est un marché considérable, la preuve: Google a rapidement était rejoint par Yahoo avec Yahoo Map et par Microsoft avec Virtual Earth. Alors est-ce réellement facile d'intégrer Google Map ou ses concurrents sur son site ? Quelles similitudes y a-t'il entre les frameworks proposés ? C'est ce que nous allons voir dans cet article.
Et pour illustrer cet article, je vous propose de créer notre mashup-application. Le sujet est facile à trouver: j'ai envie de représenter sur mon site l'emplacement des membres de ma communauté: la communauté DotNetGuru. Chaque membre sera représenté par un icône sur lequel on pourra cliquer pour avoir son nom. Comme les membres de ma communauté ont des origines différentes je veux pouvoir filtrer l'affichage selon leurs préférences (J2EE ou .NET). Je veux aussi pouvoir disposer d'une liste de mes membres et pouvoir cliquer sur un nom pour zoomer automatiquement sur son emplacement.
Enfin, cerise sur le gâteau, comme je n'y connais rien aux outils du marché et que je ne veux pas tout réécrire pour changer d'outil, je veux que cela fonctionne à la fois sur Google Map, Yahoo Map et Virtual Earth. Beau programme non ?
Listons tout d'abord les opérations de cartographie dont nous avons besoin pour créer notre application:
Nous regroupons toutes ces opérations dans une interface que nous appelerons MyMap. Voilà l'interface de MyMap en JavaScript:
// MyMap Interface void MyMapGeocode(address, callback); void MyMapInitialize(mapname, lat, lng, zoom, mode); Object MyMapAddMarker(lat, lng, markertype, info); void MyMapRemoveMarker(marker); void MyMapSetZoom(zoom); void MyMapGoto(lat, lng); void MyMapTerminate();
Pour permettre le fonctionnement sur les trois outils Google Map, Yahoo Map ou Virtual Earth, nous nous efforcerons d'implémenter l'interface MyMap sur chacune de ces APIs.
Je précise que malgré ce qu'on peut lire ou entendre et quelque soit le Framework AJAX que l'on utilise, cela nécessite d'avoir "quelques" compétences JavaScript. En particulier, pour implémenter les méthodes de MyMap il est nécessaire d'être familier avec JavaScript et notamment avec la manipulation d'objets et avec les callback.
Pour simplifier notre développement puisque nous cherchons surtout à étudier les API de cartographie, l'application que nous allons écrire va se limiter à une page HTML. Bien entendu la page pourrait aussi bien être une page ASPX, JSP ou PHP.
Voici la structure de la page:
La page se compose des éléments suivants:
Le paragraphe suivant va détailler ces éléments.
Pour pouvoir intégrer Google Map, Yahoo Map dans une page, il faut s'enregistrer au préalable auprès de l'éditeur. L'enregistrement permet de disposer d'un code d'accès qui autorise un nombre limité d'utilisations, ce nombre d'utilisation est généralement suffisant en développement ou pour une démonstration. L'utilisation de Virtual Earth ne nécessite pas d'enregistrement préalable.
Pour utiliser Google Map, il est nécessaire d'intégrer le script suivant au début de la page:
<script src="http://maps.google.com/maps?file=api&v=2&key=XBQIAAAAor5up_1nM9SKM8Rp_DseXBSbhcvIRagSHGG9ZRIE7qvWjC5W2hSk5DECk-m47NJODtlk_pUex2ZeRw" type="text/javascript"></script>
Cette ligne inclus en fait le code JavaScript de l'API Google Map après vérification d'une clé d'utilisation. La valeur de la clé key est fournie suite à l'enregistrement sur cette page du site Google. L'enregistrement impose d'indiquer le préfixe de l'URL d'où sera appelée l'application (par exemple: http://www.dotnetguru.org). Toute utilisation depuis une URL différente de celle enregistrée provoque un message d'avertissement et empêche l'affichage de la carte. Il est par contre possible d'utiliser la page localement (i.e. avec "file://").
Pour utiliser Yahoo Map, il est nécessaire d'intégrer le script suivant au début de la page:
<script src="http://api.maps.yahoo.com/ajaxymap?v=3.0&appid=YahooDemo" type="text/javascript"></script>
Là aussi cette ligne correspond à l'inclusion du code JavaScript de l'API et là aussi la valeur de appid est fournie suite à l'enregistrement sur cette page du site Yahoo.
Pour utiliser Virtual Earth, il est nécessaire d'intégrer le script suivant au début de la page:
<script src="http://dev.virtualearth.net/mapcontrol/v3/mapcontrol.js"></script>
Il n'est pas nécessaire de s'enregistrer au préalable mais l'usage est limité également (voir plus loin).
La première méthode de notre interface est la méthode de Geocoding. En effet, la première chose à faire est de connaître les coordonnées de chacun des membres de la communauté. Pour les outils qui nous intéressent, cela signifie disposer de ses coordonnées polaires: latitude et longitude. Si tout le monde connait son adresse postale, on connait plus rarement ses coordonnées polaires ! Pour connaitre ses coordonnées polaires deux solutions:
Plusieurs sites web permettent de faire du géocodage, c'est le cas par exemple de TravelGIS qui vous le propose gratuitement à partir d'une vingtaine de pays. C'est également possible par programmation à travers chacune des APIs que nous étudions.
Pour uniformiser le fonctionnement, nous décidons que notre méthode de geocodage appellera une fonction callback auquel nous transmettrons un objet MyPoint représentant les coordonnées recherchées ou null en cas d'erreur. Voici le constructeur de notre objet MyPoint JavaScript:
function MyMapPoint() { this.lat = 0; this.lng = 0; }
Etudions maintenant l'implémentation du Géocoding pour chaque API.
La classe GClientGeocoder permet via la méthode getLatLng d'obtenir les coordonnées d'un point à partir de son adresse au format "Adresse, Ville, Pays". Elle appelle une fonction callback en lui spécifiant le point trouvé ou null en cas d'erreur.
var geo = new GClientGeocoder(); function MyMapGeocode(address, callback) { geo.getLatLng(address, function(point) { if (!point) callback(null); else { res = new MyMapPoint(); res.lat = point.lat(); res.lng = point.lng(); callback(res); } }); }
La classe GMap contient une méthode geoCodeAddress permettant d'obtenir les coordonnées d'un point à partir de son adresse au format "Adresse, Ville, Pays". Une fonction callback est appelée via la création d'un événement YEvent. Attention, ce service est pour l'instant limité aux états-unis.
function MyMapGeocode(address, callback) { YEvent.Capture(map, EventsList.onEndGeoCode, function (point) { if (!point) callback(null); else { var res = new MyMapPoint(); res.lat = point.Lat; res.lng = point.Lon; callback(res); } }); map.geoCodeAddress(address); }
La classe VEMap contient une méthode Find permettant de récupérer les coordonnées d'un point à partir de son adresse au format "Adresse, Ville, Pays". Une fonction callback est appelée pour la récupération des résultats.
function MyMapGeocode(address, callback) { map.Find(null, address, 1, function(results) { if (results.length == 0) callback(null); else { var res = new MyMapPoint(); res.lat = results[0].LatLong.Latitude; res.lng = results[0].LatLong.Longitude; callback(res); } }); }
A la fin du geocoding de chacun des membres, nous avons besoin de stocker les coordonnées que nous avons récupérés. Pour cela nous utilisons la notation JSON (JavaScript Object Notation) qui permet de représenter les valeurs d'objets et de tableaux JavaScript. Voici ce que cela donne pour nos membres:
var guys = [ { "lat": 48.786290, "lng": 2.057517, "pref": ".NET", "name":"Lionel Lask\351", "company":"C2S" }, { "lat": 43.578394, "lng": 1.495859, "pref": "J2EE", "name":"Sami Jaber", "company":"Valtech" }, { "lat": 48.892756, "lng": 2.247776, "pref": "J2EE", "name":"Thomas Gil", "company":"Valtech" }, { "lat": 48.880620, "lng": 2.325784, "pref": "J2EE", "name":"Bruno Guedes", "company":"Atos Origin" }, { "lat": 48.867804, "lng": 2.359656, "pref": ".NET", "name":"Jean-Louis Besnard", "company":"Brainsonic" }, { "lat": 48.835729, "lng": 2.238051, "pref": ".NET", "name":"Eric Groise", "company":"MobiPocket" }, { "lat": 47.755057, "lng": 7.320988, "pref": ".NET", "name":"S\351bastien Ros", "company":"Evaluant" }, { "lat": 48.885187, "lng": 2.244792, "pref": "J2EE", "name":"Didier Girard", "company":"Improve" }, { "lat": 48.692796, "lng": 2.221195, "pref": ".NET", "name":"Marc Gardette", "company":"Microsoft" }, { "lat": 47.639557, "lng": -122.128336, "pref": ".NET", "name":"Bill Gates", "company":"Microsoft" } ];
Chaque membre est un élément du tableau et dispose de propriétés: latitude, longitude, nom, préférence et société.
Notre application est uniquement une page HTML et ce tableau est simplement stocké dans une variable JavaScript. Mais JSON est aussi utilisé avec AJAX pour récupérer des données en provenance d'un serveur. Dans notre cas, on pourrait imaginer qu'une page ASPX génère ce tableau à partir d'une requête en base de données. Je vous le laisse à titre d'exercice :-)
L'affichage de la carte se fait dans une balise HTML DIV. En fait, toutes les APIs fonctionnent sur le même principe: on appelle une fonction d'initialisation auquel on spécifie l'identifiant de cette balise et la balise est remplacée par la carte.
Google Map |
Yahoo Map |
Virtual Earth |
En plus de cette balise, il est également nécessaire de spécifier les coordonnées du centre de la carte, le niveau de zoom de départ et le type de carte affiché. Trois types de carte existent:
Pour uniformiser les APIs, MyMap propose des constantes spécifiques qui pointent sur les types de carte existant: MYMODE_MAP, MYMODE_SATELLITE et MYMODE_MIXTE.
A l'affichage de la carte on peut également indiquer les éléments de navigation qu'elle fait apparaître: flèches de déplacement, barre de zoom et modification du mode d'affichage. Là aussi pour uniformiser, MyMap positionne systématiquement ces éléments sur la carte.
Pour créer la carte dans Google Map, on créé simplement un objet GMap2 en lui spécifiant le contrôle DIV à utiliser. Le positionnement se fait via la méthode setCenter. On ajoute ensuite les contrôles de déplacement/barre de zoom (GLargeMapControl) et de modification du mode d'affichage (GMapTypeControl).
var MYMODE_MAP = G_NORMAL_MAP; var MYMODE_SATELLITE = G_SATELLITE_MAP; var MYMODE_MIXTE = G_HYBRID_MAP; function MyMapInitialize(mapname, lat, lng, zoom, mode) { if (GBrowserIsCompatible()) { map = new GMap2(document.getElementById(mapname)); map.setCenter(new GLatLng(lat, lng), zoom, mode); map.addControl(new GLargeMapControl()); map.addControl(new GMapTypeControl()); } }
Pour créer la carte dans Yahoo Map, on créé simplement un objet YMap en lui spécifiant le contrôle DIV à utiliser. Le positionnement se fait via la méthode setdrawZoomAndCenter (nous reviendrons plus loin sur le niveau de zoom Yahoo qui est un peu particulier). On ajoute ensuite les contrôles de déplacement, la barre de zoom et la modification du mode d'affichage.
var MYMODE_MAP = YAHOO_MAP_REG; var MYMODE_SATELLITE = YAHOO_MAP_SAT; var MYMODE_MIXTE = YAHOO_MAP_HYB; function MyMapInitialize(mapname, lat, lng, zoom, mode) { map = new YMap(document.getElementById(mapname), mode); map.drawZoomAndCenter(new YGeoPoint(lat, lng), 18-zoom); map.addPanControl(); map.addZoomLong(); map.addTypeControl(); }
Pour créer la carte dans Virtual Earth, on créé d'abord un objet VEMap en lui spécifiant l'identifiant du contrôle DIV à utiliser. Puis on appelle la méthode LoadMap qui positionne le centre, le niveau de zoom, le mode et déclenche l'affichage. Les contrôles de déplacement, barre de zoom et de modification du mode d'affichage sont affichés par défaut.
var MYMODE_MAP = 'r'; var MYMODE_SATELLITE = 'a'; var MYMODE_MIXTE = 'h'; function MyMapInitialize(mapname, lat, lng, zoom, mode) { map = new VEMap(mapname); map.LoadMap(new VELatLong(lat, lng), zoom, mode, false); }
Tout l'intérêt d'intégrer une carte dans son application est de pouvoir positionner ses propre marqueurs. Pour les trois APIs Google Map, Yahoo Map et Virtual Earth, un marqueur est représenté par une image qui est superposée à la carte. Google Map ajoute également une image d'ombre qui donne une impression de perspective. Les images sont positionnées par des coordonnées polaires. La gestion de la visibilité de l'image selon la portion de carte affichée et la transformation des coordonnées polaires en pixel sont gérés automatiquement par les APIs.
Pour MyMap, pour uniformiser l'utilisation des APIs nous spécifions un type de marqueur plutôt qu'une image. A chaque type correspond une image spécifique.
MYMARKER_TYPE1 | MYMARKER_TYPE2 |
L'ombre des marqueurs |
Sur chaque marqueur il est possible de faire apparaître des informations sous forme d'une infobulle. L'infobulle peut afficher du texte formaté HTML: balises de mise en page, tableaux voir même image. Selon l'API l'apparition de l'infobulle dépend d'un clic, du passage sur le marqueur ou d'un paramétrage.
Infobulle Google |
Infobulle Yahoo |
Infobulle Virtual Earth |
Avant de créer un marqueur dans Google Map, il faut d'abord créer un objet GIcon. On spécifie le nom et la taille de l'image et de l'ombre qu'on va utiliser et leur point d'ancrage (le point de l'image sur lequel on positionne la coordonnée polaire). Pour MyMap, on utilise une image de base que l'on décline simplement selon le type du marqueur.
La création du marqueur lui-même se fait par l'intermédiaire de l'objet GMarker. Il reçoit en paramètre les coordonnées et l'objet icône.
Pour afficher l'infobulle, on ajoute un traitement d'événement sur le marqueur (on peut choisir quel événement: click, mouseover, ...) et on appelle la méthode openInfoWindowHtml. La méthode gère automatiquement l'affichage d'une infobulle contenant du texte HTML avec son ombre.
Une fois ces étapes effectuées, la méthode addOverlay permet d'ajouter le marqueur sur la carte.
var baseIcon = new GIcon(); baseIcon.shadow = "images/shadow.png"; baseIcon.iconSize = new GSize(20, 34); baseIcon.shadowSize = new GSize(37, 34); baseIcon.iconAnchor = new GPoint(9, 34); baseIcon.infoWindowAnchor = new GPoint(9, 2); baseIcon.infoShadowAnchor = new GPoint(18, 25); var MYMARKER_TYPE1 = new GIcon(baseIcon); MYMARKER_TYPE1.image = "images/marker_red.png" var MYMARKER_TYPE2 = new GIcon(baseIcon); MYMARKER_TYPE2.image = "images/marker_blue.png" function MyMapAddMarker(lat, lng, markertype, info) { var newmarker = new GMarker(new GLatLng(lat, lng), markertype); GEvent.addListener(newmarker, "click", function() { newmarker.openInfoWindowHtml(info); }); map.addOverlay(newmarker); return newmarker; }
Pour créer un marqueur Yahoo Map, on créé tout d'abord un objet YImage. L'objet indique l'image à utiliser, sa taille et le point de l'image sur lequel on positionne les coordonnées. Ensuite, on créé un objet YMarker avec les coordonnées polaire du marqueur et l'image à utiliser.
Pour afficher une infobulle, on associe un traitement d'événement au marqueur (on peut choisir quel type d'événement: MouseClick, MouseOver, ...). Dans le traitement de l'événement on appelle la méthode openSmartWindow qui gère l'affichage de l'infobulle avec le texte formaté HTML.
Enfin, l'ajout du marqueur sur la carte se fait par appel de la méthode addOverlay.
var MYMARKER_TYPE1 = new YImage(); MYMARKER_TYPE1.src = "images/marker_red.png" MYMARKER_TYPE1.size = new YSize(20,34); MYMARKER_TYPE1.offsetSmartWindow = new YCoordPoint(9,34); var MYMARKER_TYPE2 = new YImage(); MYMARKER_TYPE2.src = "images/marker_blue.png" MYMARKER_TYPE2.size = new YSize(20,34); MYMARKER_TYPE2.offsetSmartWindow = new YCoordPoint(9,34); function MyMapAddMarker(lat, lng, markertype, info) { var marker = new YMarker(new YGeoPoint(lat, lng), markertype); YEvent.Capture(marker, EventsList.MouseClick, function OnClick() { marker.openSmartWindow(info); }); map.addOverlay(marker); return marker; }
L'ajout d'un marqueur avec Virtual Earth est plus rudimentaire qu'avec les autres APIs. On créé un objet VEPushPin avec un identifiant, les coordonnées, une image et le texte de l'infobulle, puis on l'ajoute simplement avec la méthode AddPushpin. L'affichage de l'infobulle lors du passage de la souris (on ne peut changer l'événement déclenchant) est géré automatiquement par l'API.
var MYMARKER_TYPE1 = 'images/marker_red.png'; var MYMARKER_TYPE2 = 'images/marker_blue.png'; var myPINid = 1; function MyMapAddMarker(lat, lng, markertype, info) { var pinID; var pin; pinID = myPINid; myPINid = myPINid + 1; pin = new VEPushpin( pinID, new VELatLong(lat, lng), markertype, '', info ); map.AddPushpin(pin); return pinID; }
Dans notre application de test, la suppression d'un marqueur est utilisée lors du filtrage. Pour supprimer un marqueur, la seule difficulté est d'identifier le point à supprimer. Pour Google Map et Yahoo Map, la suppression nécessite de spécifier l'objet marqueur qui a été créé, pour Virtual Earth il faut spécifier l'identifiant qu'on a attribué au marqueur à sa création.
C'est la "même" méthode removeOverlay qui permet de supprimer un marqueur sur la carte dans Google Map et Yahoo Map. Elle reçoit en paramètre l'objet GMarker/YMarker qui a été créé.
function MyMapRemoveMarker(marker) {
map.removeOverlay(marker);
}
La méthode DeletePushpin permet de supprimer un marqueur sur la carte Virtual Earth. Elle reçoit en paramètre l'identifiant du marqueur qui a été créé.
function MyMapRemoveMarker(marker) {
map.DeletePushpin(marker);
}
L'augmentation du niveau zoom permet de se rapprocher ou de s'éloigner du centre de la carte affichée. Par chance (!), les niveaux de zoom sont équivalents entre les différentes APIs Google Map, Yahoo Map et Virtual Earth. Plus précisément cette "équivalence" signifie:
Google Map niveau de zoom 6 |
Yahoo Map niveau de zoom 12 (=18-6) |
Virtual Earth niveau de zoom 6 |
Le zoom de Google Map s'exprime de 0 (le plus éloigné du sol) à 19 (le plus proche). La méthode setZoom permet de spécifier le niveau de zoom souhaité.
function MyMapSetZoom(zoom) {
map.setZoom(zoom);
}
Le zoom de Yahoo Map s'exprime de 1 (le plus proche du sol) à 16 (le plus éloigné). La méthode setZoomLevel permet de spécifier le niveau de zoom souhaité. Le zoom fonctionnant à l'inverse des autres APIs, on fait la différence à 18 pour uniformiser l'appel de la méthode.
function MyMapSetZoom(zoom) {
map.setZoomLevel(18-zoom);
}
Le zoom de Virtual Earth s'exprime de 1 (le plus éloigné du sol) à 19 (le plus proche). La méthode SetZoomLevel permet de spécifier le niveau de zoom souhaité.
function MyMapSetZoom(zoom) {
map.SetZoomLevel(zoom);
}
Le déplacement de la carte consiste à repositionner les coordonnées du centre de la zone affichée. Pour fluidifier le déplacement lorsque le point de départ et d'arrivée sont assez proche, Google Map et Virtual Earth réalisent une petite animation visuelle.
La méthode panTo permet de déplacer le centre de la carte vers l'objet GLatLng passé en paramètre.
function MyMapGoto(lat, lng) { map.panTo(new GLatLng(lat, lng)); }
La méthode panToLatLon permet de déplacer le centre de la carte vers l'objet YGeoPoint passé en paramètre.
function MyMapGoto(lat, lng) { map.panToLatLon(new YGeoPoint(lat, lng)); }
Comme son nom l'indique, la méthode SetCenter permet de déplacer le centre de la carte vers l'objet VELatLong passé en paramètre.
function MyMapGoto(lat, lng) { map.SetCenter(new VELatLong(lat, lng)); }
Pour finir proprement l'application au déchargement de la page il est intéressant de pouvoir disposer les ressources mémoires utilisées. Google Map est la seule API à proposer cette fonctionnalité. Pour uniformiser, MyMap propose une implémentation vide pour Yahoo Map et Virtual Earth.
La fonction GUnload permet d'effectuer le déchargement des ressources.
function MyMapTerminate() {
GUnload();
}
Maintenant que nous disposons d'une API permettant de réaliser nos différentes actions, il ne nous reste plus qu'à écrire le code JavaScript de notre application. Sans entrer dans le détail, voici les opérations réalisées:
Pour s'assurer de la portabilité de l'implémentation de MyMap sur Google Map, Yahoo Map et Virtual Earth, j'ai encapsulé la page dans une frame qui permet de choisir dynamiquement l'API, le code de l'application restant identique quelque soit l'API utilisée.
Cliquez sur l'image pour lancer l'application.
A noter que l'ensemble des sources de l'application et de la librairie MyMap est téléchargeable ici. La totalité de l'application est placée sous licence LGPL, vous pouvez donc le réutiliser librement dans vos développements propriétaires ou non.
Nous avons désormais une application opérationnelle mais peut-on librement utiliser les APIs Google Map, Yahoo Map et Virtual Earth ? Y a t'il une redevance à payer ? Pour le savoir il faut lire attentivement les "Terms of use" de ces APIs.
Le droit d'utilisation est assez similaire pour les différentes APIs. Pour simplifier, on peut dire que l'utilisation est autorisée gratuitement pour usage sur un site internet ouvert (pas de limitation des accès au site) et gratuit (pas d'abonnement pour accéder au site). Ce droit d'usage intègre de plus des limites:
Bref, pour une utilisation orientée entreprise, il est évident qu'il faut négocier avec l'éditeur un accès spécifique. Les trois fournisseurs le proposent. Le coût dépend de l'usage: nombre d'utilisateurs potentiels, nombre d'accès (souvent exprimé par le nombre de "briques" de carte qui vont être renvoyés). Google communique un prix public de 10 000 € pour son offre entreprise.
Cet article a présenté la manière de développer une mash-up application permettant de tirer partie des fonctionnalités de cartographie de Google Map, Yahoo Map ou Virtual Earth. A travers l'implémentation du framework portable MyMap, on constate aisément les similitudes entre ces APIs. Bien entendu nous n'avons pas parcouru l'ensemble des fonctionnalités proposées, on pourrait également s'intéresser à la possibilité de construire des itinéraires, de dessiner des lignes ou des polygones sur la carte, de disposer d'informations de traffic, de charger des points en masse depuis du XML, ... Il est aussi important de s'intéresser à la qualité des fonds de plan et de photos satellite proposés par chacun, surtout sur une zone exotique (vu des états-unis) comme la France.
Le domaine de la cartographie est fortement concurrentiel aujourd'hui comme le démontre l'effort des trois grands éditeurs Google, Yahoo et Microsoft pour se démarquer de la concurrence: soit en ajoutant leurs propres fonctionnalités, soit en luttant sur le terrain des prix. Le marché est considérable et l'ajout d'une représentation cartographique peut apporter une réelle valeur ajoutée à une application. Alors pourquoi s'en priver ?
Auteur : Lionel Laské
Lionel Laské est architecte et chef du service Nouvelles Technologies à C2S, société de services du groupe Bouygues. Il est également l'auteur de Liogo, un compilateur Logo pour .NET. Il peut être joint à l'adresse llaske@c2s.fr. |