Afficher une carte avec JavaFX - Gluon Maps et WebView

Afficher une carte avec JavaFX et Gluon Maps

C'est la solution officielle pour JavaFX. En effet, Gluon est la société qui se charge du développement de JavaFX.

Vous pouvez obtenir plus d'informations sur le site de Gluon gluonhq.com ou directement sur la page de Gluon Maps

Dépendances Maven pour Gluon Maps et JavaFX

<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-controls</artifactId>
    <version>16</version>
</dependency>
<dependency>
    <groupId>com.gluonhq.attach</groupId>
    <artifactId>util</artifactId>
    <version>4.0.11</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>com.gluonhq.attach</groupId>
    <artifactId>storage</artifactId>
    <version>4.0.11</version>
    <scope>runtime</scope>
    <classifier>desktop</classifier>
</dependency>
<dependency>
    <groupId>com.gluonhq</groupId>
    <artifactId>maps</artifactId>
    <version>2.0.0-ea+1</version>
</dependency>

Exemple Gluon Maps

import com.gluonhq.maps.MapPoint;
import com.gluonhq.maps.MapView;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class GluonMapExample extends Application {

 public static void main(String[] args) {
  launch();
 }

 @Override
 public void start(Stage stage) {

  /* Définit la plate-forme pour éviter "javafx.platform is not defined" */
  System.setProperty("javafx.platform", "desktop");

  /*
   * Définit l'user agent pour éviter l'exception
   * "Server returned HTTP response code: 403"
   */
  System.setProperty("http.agent", "Gluon Mobile/1.0.3");

  VBox root = new VBox();

  /* Création de la carte Gluon JavaFX */
  MapView mapView = new MapView();

  /* Création du point avec latitude et longitude */
  MapPoint mapPoint = new MapPoint(46.227638, 2.213749);

  /* Création et ajoute une couche à la carte */

  // MapLayer mapLayer = new CustomPinLayer(mapPoint);
  // MapLayer mapLayer = new CustomCircleMarkerLayer(mapPoint);
  // mapView.addLayer(mapLayer);

  /* Zoom de 5 */
  mapView.setZoom(5);

  /* Centre la carte sur le point */
  mapView.flyTo(0, mapPoint, 0.1);

  root.getChildren().add(mapView);

  /*
   * IMPORTANT mettre la taille de la fenêtre pour éviter l'erreur
   * java.lang.OutOfMemoryError
   */
  Scene scene = new Scene(root, 640, 480);

  stage.setScene(scene);
  stage.show();

 }

}

Exemples de couches de cartes avec Gluon Maps

Pour utiliser les couches de cartes vous pouvez utiliser le code suivant :

MapLayer mapLayer = new CustomPinLayer(mapPoint);
MapLayer mapLayer = new CustomCircleMarkerLayer(mapPoint);
mapView.addLayer(mapLayer);

Cette couche va dessiner un cercle rouge à l'emplacement du MapPoint (latitude et longitude).

import com.gluonhq.maps.MapLayer;
import com.gluonhq.maps.MapPoint;

import javafx.geometry.Point2D;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;

/** Affiche un point rouge sur la carte */
public class CustomCircleMarkerLayer extends MapLayer {

 private final MapPoint mapPoint;
 private final Circle circle;

 /**
  * @param mapPoint le point (latitude et longitude) où afficher le cercle
  * @see com.gluonhq.maps.MapPoint
  */
 public CustomCircleMarkerLayer(MapPoint mapPoint) {
  this.mapPoint = mapPoint;

  /* Cercle rouge de taille 5 */
  this.circle = new Circle(5, Color.RED);

  /* Ajoute le cercle au MapLayer */
  this.getChildren().add(circle);
 }

 /* La fonction est appelée à chaque rafraichissement de la carte */
 @Override
 protected void layoutLayer() {
  /* Conversion du MapPoint vers Point2D */
  Point2D point2d = this.getMapPoint(mapPoint.getLatitude(), mapPoint.getLongitude());

  /* Déplace le cercle selon les coordonnées du point */
  circle.setTranslateX(point2d.getX());
  circle.setTranslateY(point2d.getY());
 }
}

Pour dessiner une épingle de carte vous pouvez utiliser une image.

L'image dans le code est nommée map-pin.png

Le calcul pour placer l'épingle correctement va dépendre de la taille et de la forme de l'épingle.

import com.gluonhq.maps.MapLayer;
import com.gluonhq.maps.MapPoint;

import javafx.geometry.Point2D;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;

/** Affiche une épingle sur la carte */
public class CustomPinLayer extends MapLayer {

 private final MapPoint mapPoint;
 private final ImageView mapPinImageView;

 private static final int PIN_WIDTH = 30, PIN_HEIGHT = 30;

 /**
  * @param mapPoint le point (latitude et longitude) où afficher l'épingle
  * @see com.gluonhq.maps.MapPoint
  */
 public CustomPinLayer(MapPoint mapPoint) {
  this.mapPoint = mapPoint;

  Image image = new Image("map-pin.png", PIN_WIDTH, PIN_HEIGHT, false, false);
  this.mapPinImageView = new ImageView(image);

  /* Ajoute l'épingle au MapLayer */
  this.getChildren().add(this.mapPinImageView);
 }

 /* La fonction est appelée à chaque rafraichissement de la carte */
 @Override
 protected void layoutLayer() {
  /* Conversion du MapPoint vers Point2D */
  Point2D point2d = this.getMapPoint(mapPoint.getLatitude(), mapPoint.getLongitude());

  /* Déplace l'épingle selon les coordonnées du point */
  mapPinImageView.setTranslateX(point2d.getX() - (PIN_WIDTH / 2));
  mapPinImageView.setTranslateY(point2d.getY() - PIN_HEIGHT);
 }
}

Afficher une carte avec une Web View JavaFX et LeafletJS

Dépendances Maven JavaFX WebView

<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-controls</artifactId>
    <version>16</version>
</dependency>
<dependency>
    <groupId>org.openjfx</groupId>
    <artifactId>javafx-web</artifactId>
    <version>16</version>
</dependency>

Exemple avec une Web View JavaFX

L'avantage de la Web View c'est que vous pouvez afficher n'importe quelle carte HTML (en théorie si la carte est compatible avec le moteur web de JavaFX).

L'inconvénient c'est qu'il faudra forcément passer par JavaScript pour communiquer avec la carte.

Vous pouvez trouver le contenu HTML dans l'article sur leafletJS et OpenStreetMap

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

public class WebViewMapExample extends Application {

 public static void main(String[] args) {
  launch();
 }

 @Override
 public void start(Stage primaryStage) {

  /* Création de la WebView et du moteur */
  WebView webView = new WebView();
  WebEngine webEngine = webView.getEngine();

  /* Charge la carte HTML avec Leafletjs */
  webEngine.loadContent("<!DOCTYPE html>\n"
  + "<html lang=\"fr\">\n"
  + "\n"
  + "<head>\n"
  + "    <meta charset=\"UTF-8\">\n"
  + "    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
  + "    <title>Carte</title>\n"
  + "    <!-- leafletjs CSS -->\n"
  + "    <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.min.css\" />\n"
  + "    <!-- leafletjs JS -->\n"
  + "    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.min.js\"></script>\n"
  + "    <style>\n"
  + "        /* Carte plein écran */\n"
  + "        html,\n"
  + "        body {\n"
  + "            margin: 0;\n"
  + "            height: 100%;\n"
  + "        }\n"
  + "\n"
  + "        #map {\n"
  + "            width: 100%;\n"
  + "            height: 100%;\n"
  + "        }\n"
  + "    </style>\n"
  + "</head>\n"
  + "\n"
  + "<body>\n"
  + "\n"
  + "    <!-- L'endroit ou la carte va s'afficher -->\n"
  + "    <div id=\"map\"></div>\n"
  + "\n"
  + "    <script>\n"
  + "        /* Les options pour afficher la France */\n"
  + "        const mapOptions = {\n"
  + "            center: [46.225, 0.132],\n"
  + "            zoom: 5\n"
  + "        }\n"
  + "\n"
  + "        /* Les options pour affiner la localisation */\n"
  + "        const locationOptions = {\n"
  + "            maximumAge: 10000,\n"
  + "            timeout: 5000,\n"
  + "            enableHighAccuracy: true\n"
  + "        };\n"
  + "\n"
  + "        /* Création de la carte */\n"
  + "        var map = new L.map(\"map\", mapOptions);\n"
  + "\n"
  + "        /* Création de la couche OpenStreetMap */\n"
  + "        var layer = new L.TileLayer(\"http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\n"
  + "            { attribution: '&copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors' });\n"
  + "\n"
  + "        /* Ajoute la couche de la carte */\n"
  + "        map.addLayer(layer);\n"
  + "\n"
  + "        /* Verifie que le navigateur est compatible avec la géolocalisation */\n"
  + "        if (\"geolocation\" in navigator) {\n"
  + "            navigator.geolocation.getCurrentPosition(handleLocation, handleLocationError, locationOptions);\n"
  + "        } else {\n"
  + "            /* Le navigateur n'est pas compatible */\n"
  + "            alert(\"Géolocalisation indisponible\");\n"
  + "        }\n"
  + "\n"
  + "        function handleLocation(position) {\n"
  + "            /* Zoom avant de trouver la localisation */\n"
  + "            map.setZoom(16);\n"
  + "            /* Centre la carte sur la latitude et la longitude de la localisation de l'utilisateur */\n"
  + "            map.panTo(new L.LatLng(position.coords.latitude, position.coords.longitude));\n"
  + "        }\n"
  + "\n"
  + "        function handleLocationError(msg) {\n"
  + "            alert(\"Erreur lors de la géolocalisation\");\n"
  + "        }\n"
  + "\n"
  + "    </script>\n"
  + "\n"
  + "</body>\n"
  + "\n"
  + "</html>");
  
  Scene scene = new Scene(webView, 640, 480);
  primaryStage.setScene(scene);
  primaryStage.show();

 }

}

Commentaires