📜 ⬆️ ⬇️

Using Google Map in a JavaFX application


I want to talk about my experience using Google Maps in a JavaFX application. Consider loading a map into an application and calling Google Maps JavaScript API v3 for a loaded map from your Java code.

Implementation


First, we write the initialization code of the map in javascript, which we will issue as a html-page map.html. Further, as we move we will add additional code.
map.html
<!DOCTYPE html> <html> <head> <meta name="viewport" content="initial-scale=1.0, user-scalable=no" /> <style type="text/css"> html { height: 100% } body { height: 100%; margin: 0; padding: 0 } #map_canvas { height: 100% } </style> <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=***&sensor=false"> </script> <script type="text/javascript"> var map; var marker; function initialize() { var defLatLng = new google.maps.LatLng(59.95632093391832, 30.309906005859375); var mapOptions = { center: defLatLng, zoom: 3, mapTypeId: google.maps.MapTypeId.ROADMAP, disableDefaultUI: true, panControl: false }; map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions); marker = new google.maps.Marker({ position: defLatLng, map: map, icon: "img/Pin.png" }); } </script> </head> <body onload="initialize()"> <div id="map_canvas" style="width:100%; height:100%"></div> </body> </html> 


Now we have an html-page with a map. It can be opened in any browser. JavaFX has a great class for displaying web content: javafx.scene.web.WebView. We will use it to display the written html-page. Let's write a GoogleMap class that will represent a Google Map in our application in JavaFX. To work with the map as with any other JavaFX node (Scene graph node), we will inherit from the javafx.scene.Parent class.
GoogleMap.java
 public class GoogleMap extends Parent { public GoogleMap() { initMap(); getChildren().add(webView); } private void initMap() { webView = new WebView(); webEngine = webView.getEngine(); webEngine.load(getClass().getResource("map.html").toExternalForm()); ready = false; webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() { @Override public void changed(final ObservableValue<? extends Worker.State> observableValue, final Worker.State oldState, final Worker.State newState) { if (newState == Worker.State.SUCCEEDED) { ready = true; } } }); } private WebView webView; private WebEngine webEngine; private boolean ready; } 


A simple map display is not enough. We want to manage the map and catch map events. To access functions in javascript, we will need an instance of the netscape.javascript.JSObject class, which we will get in the initCommunication () method, called in the GoogleMap class constructor. Also in this method, let’s cast the javascript code of GoogleMap.this (an instance of the GoogleMap class) into the context in order to be able to call the methods of the GoogleMap class from the javascript code.
initCommunication ()
  private void initCommunication() { webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() { @Override public void changed(final ObservableValue<? extends Worker.State> observableValue, final Worker.State oldState, final Worker.State newState) { if (newState == Worker.State.SUCCEEDED) { doc = (JSObject) webEngine.executeScript("window"); doc.setMember("app", GoogleMap.this); } } }); } private JSObject doc; 


Let us demonstrate how to call Java code from javascript code on the example of capturing a click event on a map. This event, oddly enough, occurs when you click on the map and contains the geographical coordinates of the click. First we write the event class:
MapEvent.java
 public class MapEvent extends Event { public MapEvent(GoogleMap map, double lat, double lng) { super(map, Event.NULL_SOURCE_TARGET, Event.ANY); this.lat = lat; this.lng = lng; } public double getLat() { return this.lat; } public double getLng() { return this.lng; } private double lat; private double lng; } 


Now we’ll write the GoogleMap class methods responsible for registering the handler, receiving the event from the javasript code and calling the handler:
GoogleMap class methods
  public void setOnMapLatLngChanged(EventHandler<MapEvent> eventHandler) { onMapLatLngChanged = eventHandler; } public void handle(double lat, double lng) { if(onMapLatLngChanged != null) { MapEvent event = new MapEvent(this, lat, lng); onMapLatLngChanged.handle(event); } } private EventHandler<MapEvent> onMapLatLngChanged; 


It remains only to write the map event handler in javascript code and call the GoogleMap class handle (double lat, double lng) method in it:
get_click_position (event)
 function get_click_position(event){ var location = event.latLng; var lat = location.lat(); var lng = location.lng(); app.handle(lat, lng); } 


Now we’ll write a GoogleMap class method to call javascript functions from java code:
invokeJS (final String str)
  private void invokeJS(final String str) { if(ready) { doc.eval(str); } else { webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() { @Override public void changed(final ObservableValue<? extends Worker.State> observableValue, final Worker.State oldState, final Worker.State newState) { if (newState == Worker.State.SUCCEEDED) { doc.eval(str); } } }); } } 


Result


I will not separately explain the installation methods of the map type, the position of the center of the map, the position of the cursor. All these methods are in full source code:
GoogleMap.java
 public class GoogleMap extends Parent { public GoogleMap() { initMap(); initCommunication(); getChildren().add(webView); setMarkerPosition(0,0); setMapCenter(0, 0); switchTerrain(); } private void initMap() { webView = new WebView(); webEngine = webView.getEngine(); webEngine.load(getClass().getResource("resources/map.html").toExternalForm()); ready = false; webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() { @Override public void changed(final ObservableValue<? extends Worker.State> observableValue, final Worker.State oldState, final Worker.State newState) { if (newState == Worker.State.SUCCEEDED) { ready = true; } } }); } private void initCommunication() { webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() { @Override public void changed(final ObservableValue<? extends Worker.State> observableValue, final Worker.State oldState, final Worker.State newState) { if (newState == Worker.State.SUCCEEDED) { doc = (JSObject) webEngine.executeScript("window"); doc.setMember("app", GoogleMap.this); } } }); } private void invokeJS(final String str) { if(ready) { doc.eval(str); } else { webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() { @Override public void changed(final ObservableValue<? extends Worker.State> observableValue, final Worker.State oldState, final Worker.State newState) { if (newState == Worker.State.SUCCEEDED) { doc.eval(str); } } }); } } public void setOnMapLatLngChanged(EventHandler<MapEvent> eventHandler) { onMapLatLngChanged = eventHandler; } public void handle(double lat, double lng) { if(onMapLatLngChanged != null) { MapEvent event = new MapEvent(this, lat, lng); onMapLatLngChanged.handle(event); } } public void setMarkerPosition(double lat, double lng) { String sLat = Double.toString(lat); String sLng = Double.toString(lng); invokeJS("setMarkerPosition(" + sLat + ", " + sLng + ")"); } public void setMapCenter(double lat, double lng) { String sLat = Double.toString(lat); String sLng = Double.toString(lng); invokeJS("setMapCenter(" + sLat + ", " + sLng + ")"); } public void switchSatellite() { invokeJS("switchSatellite()"); } public void switchRoadmap() { invokeJS("switchRoadmap()"); } public void switchHybrid() { invokeJS("switchHybrid()"); } public void switchTerrain() { invokeJS("switchTerrain()"); } public void startJumping() { invokeJS("startJumping()"); } public void stopJumping() { invokeJS("stopJumping()"); } public void setHeight(double h) { webView.setPrefHeight(h); } public void setWidth(double w) { webView.setPrefWidth(w); } public ReadOnlyDoubleProperty widthProperty() { return webView.widthProperty(); } private JSObject doc; private EventHandler<MapEvent> onMapLatLngChanged; private WebView webView; private WebEngine webEngine; private boolean ready; } 


map.html
 <!DOCTYPE html> <html> <head> <meta name="viewport" content="initial-scale=1.0, user-scalable=no" /> <style type="text/css"> html { height: 100% } body { height: 100%; margin: 0; padding: 0 } #map_canvas { height: 100% } </style> <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=***&sensor=false"> </script> <script type="text/javascript"> var map; var marker; function get_click_position(event){ var location = event.latLng; var lat = location.lat(); var lng = location.lng(); setMarkerPosition(lat, lng); app.handle(lat, lng); } function setMarkerPosition(lat, lng) { var clickLatLng = new google.maps.LatLng(lat, lng); marker.setPosition(clickLatLng); } function startJumping(){ marker.setAnimation(google.maps.Animation.BOUNCE); } function stopJumping(){ marker.setAnimation(google.maps.Animation.BOUNCE); } function setMapCenter(lat, lng) { var latlng = new google.maps.LatLng(lat, lng); map.setCenter(latlng); } function switchSatellite() { var mapOptions = { mapTypeId: google.maps.MapTypeId.SATELLITE }; map.setOptions(mapOptions); setLightMarkerIcon(); } function switchRoadmap() { var mapOptions = { mapTypeId: google.maps.MapTypeId.ROADMAP }; map.setOptions(mapOptions); setDarkMarkerIcon(); } function switchHybrid() { var mapOptions = { mapTypeId: google.maps.MapTypeId.HYBRID }; map.setOptions(mapOptions); setLightMarkerIcon(); } function switchTerrain() { var mapOptions = { mapTypeId: google.maps.MapTypeId.TERRAIN }; map.setOptions(mapOptions); setDarkMarkerIcon(); } function initialize() { var defLatLng = new google.maps.LatLng(59.95632093391832, 30.309906005859375); var mapOptions = { center: defLatLng, zoom: 3, mapTypeId: google.maps.MapTypeId.ROADMAP, disableDefaultUI: true, panControl: false }; map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions); google.maps.event.addListener(map, 'click', get_click_position); marker = new google.maps.Marker({ position: defLatLng, map: map, icon: "img/Pin.png" }); app.handle(0, 0); } function setDarkMarkerIcon() { marker.setIcon("img/Pin.png"); } function setLightMarkerIcon() { marker.setIcon("img/Pin_s.png"); } </script> </head> <body onload="initialize()"> <div id="map_canvas" style="width:100%; height:100%"></div> </body> </html> 


As Voltaire said:
“I may not agree with your opinion, but I am ready to give my life for your right to express it.”

')

Source: https://habr.com/ru/post/170141/


All Articles