Использование Google Map в приложении на JavaFX

от автора


Хочу рассказать о своем опыте использования Google Maps в приложении на JavaFX. Рассмотрим загрузку карты в приложение и вызов Google Maps JavaScript API v3 для загруженной карты из своего кода на Java.

Реализация

Сначала напишем код инициализации карты на javascript, который оформим ввиде html-страницы map.html. Далее по мере продвижения будем добавлять дополнительный код.

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> 

Теперь у нас есть html-страница с картой. Ее можно открыть в любом браузере. В JavaFX есть замечательный класс для отображения веб-контента: javafx.scene.web.WebView. Воспользуемся им для отображения написанной html-странички. Напишем класс GoogleMap, который будет представлять Google Map в нашем приложении на JavaFX. Чтобы работать с картой как с любым другим JavaFX-нодом (Scene graph node), унаследуемся от класса javafx.scene.Parent.

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; } 

Простого отображения карты не достаточно. Нам хочется управлять картой и отлавливать события карты. Для обращения к функциям на javascript нам потребуется экземпляр класса netscape.javascript.JSObject, который получим в методе initCommunication(), вызываемом в конструкторе класса GoogleMap. Так же в этом методе закинем в контекст javascript кода GoogleMap.this (экземпляр класса GoogleMap), чтобы иметь возможность вызывать методы класса GoogleMap из кода на javascript.

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; 

Продемонстрируем вызов Java кода из javascript кода на примере отлавливания события клика по карте. Это событие, как ни странно, возникает при нажатии на карту и содержит географические координаты нажатия. Сначала напишем класс события:

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; } 

Теперь напишем методы класса GoogleMap, ответственные за регистрацию обработчика, прием события от javasript кода и вызов обработчика:

методы класса GoogleMap

    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; 

Осталось только написать обработчик события карты в javascript коде и вызвать в нем метод handle(double lat, double lng) класса GoogleMap:

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);  } 

Теперь напишем метод класса GoogleMap для вызова функций javascript из java кода:

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);                     }                 }             });         }     } 

Результат

Не буду отдельно пояснять методы установки типа карты, положения центра карты, положения курсора. Все эти методы есть в полных исходниках:

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> 

Как сказал Вольтер:
«Я могу быть не согласным с Вашим мнением, но я готов отдать жизнь за Ваше право высказывать его.»

ссылка на оригинал статьи http://habrahabr.ru/post/170141/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *