Introduction

Cet article s’adresse aux développeurs cherchant intégrer un système cartographique dans leur application Java. Vous trouverez ci-bas les résultats de mes recherches effectués lors de mes charges de laboratoire du cours Analyse et Conception de Logiciel l’École de Technologie Supérieure (ÉTS).

Survol des possibilités étudiées

Voici un schéma qui résume les options possibles. La légende est la suivante : en vert les options faciles, en orange difficulté moyenne, en rouge non-recommandée, et en grisées les options non explorées lors de cette recherche.

Détails

Voici un tableaux résumant les entrées et sorties pour chacune des méthodes :

Méthode Entrées Sorties Autres compétences requises
SWT JavaScript ou URL HTML
DJ NativeSwing JavaScript ou URL HTML
QT Jambi JavaScript ou URL HTML QT
Projet Web JavaScript ou URL HTML HTML, CSS, Programmation serveur (PHP,JSP,etc.)
Static Maps API + KML KML Image
Static Maps API + HTTP String Image HTTP et (XML ou JSON)
GWT String HTML Google Web Toolkit (GWT)
Flash String Flash ActionScript (AS3)

 

Dans cette solution, il s’agit d’utiliser un des différents contrôles Web existants et d’incorporer carrément un navigateur web dans l’application. Par la suite il suffit d’utiliser JavaScript ou d’envoyer les bons paramètres dans l’URL pour afficher les directions.

Si j’avais construire une application de bureau professionnelle, je choisirait le Maps API for Flash (via Adobe Air) si possible car la librairie est très stable ou j’explorerais les possibilités avec le Google Maps for Mobile (recommandé par Sun) ou la NASA World Wind (demos)

QT Jambi

QT est une librairie graphique (GUI framework) c++, open source et multiplateforme (windows, mac, linux, mobile), développée par Nokia. QT Jambi est un “port” de la librairie QT pour Java. Pas besoin de QT pour utiliser QT Jambi, il suffit de télécharger les bindings pour votre système et le QT Eclipse Integration for Jambi (Java) Plugin. Notez que pour l’installation du plugin Eclipse sur Windows 64 bits, il faut télécharger les 2 fichiers mentionnés ci-haut en 32 bits.

Voir mon article précédent pour voir comment utiliser QT Jambi dans Eclipse

Sortie HTML

L’avantage d’avoir une sortie HTML est qu’il est possible de ré-utiliser les widgets du site de Google Maps. Par contre, l’intégration d’un navigateur embarqué dans les applications de bureau (ie: traditionnelle) frôle le bris de la license de Google. Toutefois, cela est parfaitement légal pour la création d’un site internet.

Maps Data API

J’ai perdu plusieurs heures infructueuses avec l’API officielle de Google (Maps Data API, maintenant désuète). Les exemples officiels requièrent l’installation d’un grand nombre de librairies mal documentées – laissez-moi savoir si vous y arrivez. J’ai alors cherché pour un autre exemple qui me semblait complet (dont j’ai perdu l’adresse), mais sa compilation provoquait une erreur de signature dans l’appel d’une des classes de l’API.

Point de départ pour la solution avec Microsoft

(source) : Microsoft has a collaboration with USGS at http://terraserver-usa.com/. There is a freely available web service with a WSDL. You can use common Java WSDL binding libraries like Axis or CXF to create java object to access the service.

Static Maps API + HTTP

Voici un extrait de code pour cette solution :

/**
 * Copyright (c) 2010, Maxime Caumartin, David Martineau
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the names of the developpers nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package ca.etsmtl.log210.laboratoire1.GIS;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Toolkit;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.LinkedHashMap;
import utilities.json.JSONArray;
import utilities.json.JSONException;
import utilities.json.JSONObject;
import utilities.json.JSONTokener;
import utilities.polylineEncoder.PolylineEncoder;
import utilities.polylineEncoder.Track;
import utilities.polylineEncoder.Trackpoint;
import ca.etsmtl.log210.laboratoire1.modele.GestionnaireGIS;
public class GoogleMapProvider implements GestionnaireGIS {
  private HashMap<String, String> chose = new LinkedHashMap<String, String>();
  private HashMap<String, String> autreChose = new HashMap<String, String>();
  private Dimension dimmensionCarte;
  private URL polyencodedMap;
  // [...]
  @Override
  public Image getImage() throws IOException, InvalidLocalisationException {
    StringBuffer b = new StringBuffer(50);
    Image img;
    for (String s : chose.keySet())
      b.append(s).append('\n');
    if ((img = cache.get(b.toString())) == null)
    {
    JSONTokener token = null;
    URL u;
    URLConnection c;
    try
    {
      Track t = new Track();
      for(int i = 0; i < getLieux().length-1; i++){
        u = new URL("http://maps.google.com/maps/nav?q=from:"+URLEncoder.encode(getLieux()[i], "UTF-8")+"%20to:"+URLEncoder.encode(getLieux()[i+1], "UTF-8"));
        c = u.openConnection();
        // Je suis un navigateur web et mon nom est Firefox 3.6
        c.setRequestProperty("User-Agent", "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2) Gecko/20100122 Fedora/3.6.1-1.fc13 Firefox/3.6");
        c.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
        c.setRequestProperty("Accept-Language", "fr-ca,en;q=0.5");
        InputStreamReader ob = new InputStreamReader (c.getInputStream());
        token = new JSONTokener(ob);
        JSONObject o = new JSONObject(token);
        // Dans le protocole HTTP, 200 signifie que notre requete a ete executee avec succes
        if (o.getJSONObject("Status").getInt("code") != 200)
          throw new InvalidLocalisationException();
        JSONArray arrDep = o.getJSONArray("Placemark");
        JSONObject o1 = (JSONObject) arrDep.get(0);
        t.addTrackpoint(getTractPointFromJSON(o1));
        JSONArray dir = o.getJSONObject("Directions").getJSONArray("Routes")
          .getJSONObject(0).getJSONArray("Steps");
        for (int j = 0 ; j < dir.length(); j++)
          t.addTrackpoint(getTractPointFromJSON((JSONObject)dir.get(j)));
        t.addTrackpoint(getTractPointFromJSON((JSONObject)arrDep.get(1)));
      }
      HashMap<?, ?> hm = PolylineEncoder.createEncodings(t, 10, 1);
      polyencodedMap = new URL(generatePolyencodedMapURL(hm));
    }
    catch (JSONException e)
    {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
      img = Toolkit.getDefaultToolkit().createImage(polyencodedMap);
      cache.put(b.toString(), img);
    }
    return  img;
  }
  public String generatePolyencodedMapURL(HashMap<?, ?> hm) throws UnsupportedEncodingException{
    String polyEncURL;
    //"http://maps.google.com/maps/api/staticmap?sensor=false&size=800x800&markers=color:blue                     |label:D|H3C+1K3          &markers=color:blue                    |label:F|<Destination!>                     &maptype=roadmap&format=png32&path=weight:5                     |color:blue                  |enc"+hm.keySet().iterator().next().toString()
    polyEncURL = "http://maps.google.com/maps/api/staticmap?sensor=false&size=1280x1024&"+generatePolylineMarkers()+"maptype=roadmap&format=png32&path=weight:"+getLargeurChemin()+"|color:"+getCouleurChemin()+"|enc:" + hm.values().iterator().next().toString();
    return polyEncURL;
  }
  public String generatePolylineMarkers() throws UnsupportedEncodingException{
    String polyMarkers = "";
    for(int i = 0; i < getLieux().length; i++){
      polyMarkers = polyMarkers + "markers=color:"+getCouleurMarqueur()+"|label:"+this.chose.get(getLieux()[i])+"|"+URLEncoder.encode(getLieux()[i], "UTF-8")+"&#038;";
    }
    return polyMarkers;
  }
  private static Trackpoint getTractPointFromJSON(JSONObject o1) throws JSONException
  {
    JSONObject o2 = o1.getJSONObject("Point");
    JSONArray coor1 = o2.getJSONArray("coordinates");
    return new Trackpoint(coor1.getDouble(1), coor1.getDouble(0), coor1.getDouble(2));
  }
  @Override
  public String[] getLieux() {
    int i = 0;
    String [] tmpStringArray = new String[chose.keySet().size()];
    for (String s : chose.keySet())
      tmpStringArray[i++] = s;
    return tmpStringArray;
  }
  // [...]
}

Références

Crédits

Merci à Maxime de Caumartin pour la contribution de son code Java pour le GoogleMapProvider, et aux autres étudiants pour leurs contributions.