Mapping Directions with JavaFX using the GMapsFX Directions API

Mapping directions in a JavaFX application is easy with the Directions API that was recently introduced in GMapsFX.  In this blog post I’ll walk through an example of setting up an application with a map and a couple of text fields, one which will be used for the trip origin and the second which will be used for the trip destination.  When the user hits ‘Enter’ in the destination text field, the map will display the directions.

Starting off with the FXML file, we have an AnchorPane which contains the GoogleMapView and 2 TextFields.  The AnchorPane has a controller assigned to it named FXMLController, and both components have an FX ID associated with them so they will be accessible from the FXMLController class.  Also, the destination TextField has an action, “toTextFieldAction” associated with it, so this method will be called when the user hits the ‘Enter’ key in the TextField.

<?xml version="1.0" encoding="UTF-8"?>
<?import com.lynden.gmapsfx.GoogleMapView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="443.0" prefWidth="711.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.65" fx:controller="com.lynden.gmaps.directions.FXMLController">
<children>
<GoogleMapView fx:id="mapView" layoutX="-311.0" layoutY="-244.0" prefWidth="490.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
<TextField fx:id="fromTextField" prefHeight="27.0" prefWidth="222.0" promptText="From:" AnchorPane.leftAnchor="10.0" AnchorPane.topAnchor="10.0" />
<TextField fx:id="toTextField" layoutX="10.0" layoutY="10.0" onAction="#toTextFieldAction" prefHeight="27.0" prefWidth="222.0" promptText="To:" AnchorPane.leftAnchor="10.0" AnchorPane.topAnchor="50.0" />
</children>
</AnchorPane>

view raw
Scene.fxml
hosted with ❤ by GitHub

 

The result should look as follows:

Screen Shot 2016-08-19 at 2.56.27 PM

 

Next, I’ve cut up the relevant parts of the FXMLController class.  The MapComponentInitializedListener interface needs to be implemented by the controller since the underlying GoogleMap doesn’t get initialized immediately.  The DirectionsServiceCallback interface also needs to be implemented, although in this example I won’t be doing anything with it.

The GoogleMapView and the TextFields components from the FXML file are defined below and annotated with @FXML.

There is also a reference to the Directions Service as well as StringProperties to represent the ‘to’ and ‘from’ endpoints that the user will enter.

 

public class FXMLController implements Initializable, MapComponentInitializedListener, DirectionsServiceCallback {
protected StringProperty from = new SimpleStringProperty();
protected StringProperty to = new SimpleStringProperty();
@FXML
protected GoogleMapView mapView;
@FXML
protected TextField fromTextField;
@FXML
protected TextField toTextField;

view raw
FXMLController.java
hosted with ❤ by GitHub

 

After the controller is created, its initialize method is called which will set the MapView’s initialization listener to the FXMLController as well as bind the ‘to’ and ‘from’ String properties to the TextProperties of their respective TextFields.

@Override
public void initialize(URL url, ResourceBundle rb) {
mapView.addMapInializedListener(this);
to.bindBidirectional(toTextField.textProperty());
from.bindBidirectional(fromTextField.textProperty());
}

view raw
FXMLController.java
hosted with ❤ by GitHub

 

Once the map has been initialized, the DirectionService can be instantiated as well as a MapOptions object to set various attributes about the map.  The options are then configured and a GoogleMap object can be instantiated from the map view.  The directionsPane is a component which can be used to render the step by step direction text, in this example however, it won’t be displayed.

@Override
public void mapInitialized() {
MapOptions options = new MapOptions();
options.center(new LatLong(47.606189, 122.335842))
.zoomControl(true)
.zoom(12)
.overviewMapControl(false)
.mapType(MapTypeIdEnum.ROADMAP);
GoogleMap map = mapView.createMap(options);
directionsService = new DirectionsService();
directionsPane = mapView.getDirec();
}

view raw
FXMLController.java
hosted with ❤ by GitHub

Finally, the action method defined in the FXML file when the user hits ‘Enter’ in the TextField is below.  The method will call the getRoute() method on the DirectionsService class, passing in a boolean value which will define whether the route can be modified by dragging it, the map object, and the DirectionsRequest object.

@FXML
private void toTextFieldAction(ActionEvent event) {
DirectionsRequest request = new DirectionsRequest(from.get(), to.get(), TravelModes.DRIVING);
directionsService.getRoute(request, this, new DirectionsRenderer(true, mapView.getMap(), directionsPane));
}

view raw
FXMLController.java
hosted with ❤ by GitHub

 

Below is an example when the user enters directions from Seattle to Redmond

 

Screen Shot 2016-08-19 at 3.37.38 PM

That’s it!  For completeness I’ll include the full source code of the example below.

 

 

Scene.fxml

<?xml version="1.0" encoding="UTF-8"?>
<?import com.lynden.gmapsfx.GoogleMapView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="443.0" prefWidth="711.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.65" fx:controller="com.lynden.gmaps.directions.FXMLController">
<children>
<GoogleMapView fx:id="mapView" layoutX="-311.0" layoutY="-244.0" prefWidth="490.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
<TextField fx:id="fromTextField" prefHeight="27.0" prefWidth="222.0" promptText="From:" AnchorPane.leftAnchor="10.0" AnchorPane.topAnchor="10.0" />
<TextField fx:id="toTextField" layoutX="10.0" layoutY="10.0" onAction="#toTextFieldAction" prefHeight="27.0" prefWidth="222.0" promptText="To:" AnchorPane.leftAnchor="10.0" AnchorPane.topAnchor="50.0" />
</children>
</AnchorPane>

view raw
Scene.fxml
hosted with ❤ by GitHub

 

MainApp.java

package com.lynden.gmaps.directions;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class MainApp extends Application {
@Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/fxml/Scene.fxml"));
Scene scene = new Scene(root);
scene.getStylesheets().add("/styles/Styles.css");
stage.setTitle("JavaFX and Maven");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}

view raw
FXMLController.java
hosted with ❤ by GitHub

 

FXMLController.java

package com.lynden.gmaps.directions;
import com.lynden.gmapsfx.GoogleMapView;
import com.lynden.gmapsfx.MapComponentInitializedListener;
import com.lynden.gmapsfx.javascript.object.*;
import com.lynden.gmapsfx.service.directions.*;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TextField;
public class FXMLController implements Initializable, MapComponentInitializedListener, DirectionsServiceCallback {
protected DirectionsService directionsService;
protected DirectionsPane directionsPane;
protected StringProperty from = new SimpleStringProperty();
protected StringProperty to = new SimpleStringProperty();
@FXML
protected GoogleMapView mapView;
@FXML
protected TextField fromTextField;
@FXML
protected TextField toTextField;
@FXML
private void toTextFieldAction(ActionEvent event) {
DirectionsRequest request = new DirectionsRequest(from.get(), to.get(), TravelModes.DRIVING);
directionsService.getRoute(request, this, new DirectionsRenderer(true, mapView.getMap(), directionsPane));
}
@Override
public void directionsReceived(DirectionsResult results, DirectionStatus status) {
}
@Override
public void initialize(URL url, ResourceBundle rb) {
mapView.addMapInializedListener(this);
to.bindBidirectional(toTextField.textProperty());
from.bindBidirectional(fromTextField.textProperty());
}
@Override
public void mapInitialized() {
MapOptions options = new MapOptions();
options.center(new LatLong(47.606189, 122.335842))
.zoomControl(true)
.zoom(12)
.overviewMapControl(false)
.mapType(MapTypeIdEnum.ROADMAP);
GoogleMap map = mapView.createMap(options);
directionsService = new DirectionsService();
directionsPane = mapView.getDirec();
}
}

view raw
FXMLController.java
hosted with ❤ by GitHub

GMapsFX 2.0.9 Released

The latest version of GMapsFX has been released which contains a fix for a bug that was preventing the GoogleMapView component from being added as a custom component to SceneBuilder.

The fix will allow the MapView to be added as a custom component.  In a future blog post I will detail how to do this.

 

Screen Shot 2016-08-05 at 4.26.39 PM.png

Mapping an Address with JavaFX using the GMapsFX Geocoding API

Mapping an address in a JavaFX application is extremely easy with the Geocoding API that was recently introduced in GMapsFX.  In this blog post I’ll walk through an example of setting up an application with a map and a text field.  The map will recenter itself at whatever address or place the user types in the text field.

Starting off with the FXML file, we have an AnchorPane which contains the GoogleMapView and a TextField.  The AnchorPane has a controller assigned to it named FXMLController, and both components have an FX ID associated with them so they will be accessible from the FXMLController class.  Also, the TextField has an action, “addressTextFieldAction” associated with it, so this method will be called when the user hits the ‘Enter’ key in the TextField.

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.control.*?>
<?import com.lynden.gmapsfx.GoogleMapView?>
<AnchorPane id="AnchorPane" prefHeight="500" prefWidth="750" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.65" fx:controller="com.mycompany.gmapstestproject.FXMLController">
<children>
<GoogleMapView fx:id="mapView" prefHeight="500.0" prefWidth="700.0" AnchorPane.bottomAnchor="0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0" AnchorPane.topAnchor="0.0"/>
<TextField fx:id="addressTextField" onAction="#addressTextFieldAction" prefHeight="27.0" prefWidth="274.0" promptText="Address" AnchorPane.leftAnchor="10" AnchorPane.topAnchor="10" />
</children>
</AnchorPane>

view raw
Scene.fxml
hosted with ❤ by GitHub

 

The result should look as follows:

Screen Shot 2016-07-29 at 3.12.18 PM.png

 

Next, I’ve cut up the relevant parts of the FXMLController class.  The MapComponentInitializedListener interface needs to be implemented by the controller since the underlying GoogleMap doesn’t get initialized immediately.  The GoogleMapView and TextField components from the FXML file are defined below and annotated with @FXML.

There is also a reference to the GeocodingService as well as a StringProperty to represent the address the user enters.

 

public class FXMLController implements Initializable, MapComponentInitializedListener {
@FXML
private GoogleMapView mapView;
@FXML
private TextField addressTextField;
private GoogleMap map;
private GeocodingService geocodingService;
private StringProperty address = new SimpleStringProperty();

view raw
FXMLController.java
hosted with ❤ by GitHub

 

After the controller is created its initialize method is called which will set the MapView’s initialization listener to the FXMLController as well as bind the address property to the address TextField’s text property.

@Override
public void initialize(URL url, ResourceBundle rb) {
mapView.addMapInializedListener(this);
address.bind(addressTextField.textProperty());
}

view raw
FXMLController.java
hosted with ❤ by GitHub

 

Once the map has been initialized, the GeocodingService can be instantiated as well as a MapOptions object to set various attributes about the map.  Once the options are configured, a GoogleMap object can be instantiated from the map view.

@Override
public void mapInitialized() {
geocodingService = new GeocodingService();
MapOptions mapOptions = new MapOptions();
mapOptions.center(new LatLong(47.6097, 122.3331))
.mapType(MapTypeIdEnum.ROADMAP)
.overviewMapControl(false)
.panControl(false)
.rotateControl(false)
.scaleControl(false)
.streetViewControl(false)
.zoomControl(false)
.zoom(12);
map = mapView.createMap(mapOptions);
}

view raw
FXMLController.java
hosted with ❤ by GitHub

 

Finally, the action method defined in the FXML file when the user hits ‘Enter’ in the TextField is below.  The method will call the geocode() method on the GeocodeService class, passing in the value of the Address property as well as a callback method.

The callback will check the status of the results, and based on the outcome, will recenter the map at the latitude/longitude the user had entered.

@FXML
public void addressTextFieldAction(ActionEvent event) {
geocodingService.geocode(address.get(), (GeocodingResult[] results, GeocoderStatus status) > {
LatLong latLong = null;
if( status == GeocoderStatus.ZERO_RESULTS) {
Alert alert = new Alert(Alert.AlertType.ERROR, "No matching address found");
alert.show();
return;
} else if( results.length > 1 ) {
Alert alert = new Alert(Alert.AlertType.WARNING, "Multiple results found, showing the first one.");
alert.show();
latLong = new LatLong(results[0].getGeometry().getLocation().getLatitude(), results[0].getGeometry().getLocation().getLongitude());
} else {
latLong = new LatLong(results[0].getGeometry().getLocation().getLatitude(), results[0].getGeometry().getLocation().getLongitude());
}
map.setCenter(latLong);
});
}

view raw
FXMLController.java
hosted with ❤ by GitHub

 

Below is an example when the user enters New York City as the address.

Screen Shot 2016-07-29 at 4.27.56 PM

 

That’s it!  For completeness I’ll include the full source code of the example below.

 

 

Scene.fxml

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.control.*?>
<?import com.lynden.gmapsfx.GoogleMapView?>
<AnchorPane id="AnchorPane" prefHeight="500" prefWidth="750" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.65" fx:controller="com.mycompany.gmapstestproject.FXMLController">
<children>
<GoogleMapView fx:id="mapView" prefHeight="500.0" prefWidth="700.0" AnchorPane.bottomAnchor="0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0" AnchorPane.topAnchor="0.0"/>
<TextField fx:id="addressTextField" onAction="#addressTextFieldAction" prefHeight="27.0" prefWidth="274.0" promptText="Address" AnchorPane.leftAnchor="10" AnchorPane.topAnchor="10" />
</children>
</AnchorPane>

view raw
Scene.fxml
hosted with ❤ by GitHub

 

MainApp.java

package com.mycompany.gmapstestproject;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class MainApp extends Application {
@Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/fxml/Scene.fxml"));
Scene scene = new Scene(root);
stage.setTitle("JavaFX Geocode Example");
stage.setScene(scene);
stage.show();
}
/**
* The main() method is ignored in correctly deployed JavaFX application.
* main() serves only as fallback in case the application can not be
* launched through deployment artifacts, e.g., in IDEs with limited FX
* support. NetBeans ignores main().
*
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}

view raw
MainApp.java
hosted with ❤ by GitHub

 

FXMLController.java

package com.mycompany.gmapstestproject;
import com.lynden.gmapsfx.GoogleMapView;
import com.lynden.gmapsfx.MapComponentInitializedListener;
import com.lynden.gmapsfx.javascript.object.*;
import com.lynden.gmapsfx.service.geocoding.GeocoderStatus;
import com.lynden.gmapsfx.service.geocoding.GeocodingResult;
import com.lynden.gmapsfx.service.geocoding.GeocodingService;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Alert;
import javafx.scene.control.TextField;
public class FXMLController implements Initializable, MapComponentInitializedListener {
@FXML
private GoogleMapView mapView;
@FXML
private TextField addressTextField;
private GoogleMap map;
private GeocodingService geocodingService;
private StringProperty address = new SimpleStringProperty();
@Override
public void initialize(URL url, ResourceBundle rb) {
mapView.addMapInializedListener(this);
address.bind(addressTextField.textProperty());
}
@Override
public void mapInitialized() {
geocodingService = new GeocodingService();
MapOptions mapOptions = new MapOptions();
mapOptions.center(new LatLong(47.6097, 122.3331))
.mapType(MapTypeIdEnum.ROADMAP)
.overviewMapControl(false)
.panControl(false)
.rotateControl(false)
.scaleControl(false)
.streetViewControl(false)
.zoomControl(false)
.zoom(12);
map = mapView.createMap(mapOptions);
}
@FXML
public void addressTextFieldAction(ActionEvent event) {
geocodingService.geocode(address.get(), (GeocodingResult[] results, GeocoderStatus status) > {
LatLong latLong = null;
if( status == GeocoderStatus.ZERO_RESULTS) {
Alert alert = new Alert(Alert.AlertType.ERROR, "No matching address found");
alert.show();
return;
} else if( results.length > 1 ) {
Alert alert = new Alert(Alert.AlertType.WARNING, "Multiple results found, showing the first one.");
alert.show();
latLong = new LatLong(results[0].getGeometry().getLocation().getLatitude(), results[0].getGeometry().getLocation().getLongitude());
} else {
latLong = new LatLong(results[0].getGeometry().getLocation().getLatitude(), results[0].getGeometry().getLocation().getLongitude());
}
map.setCenter(latLong);
});
}
}

view raw
FXMLController.java
hosted with ❤ by GitHub

GMapsFX 2.0.7 Released

A new version of GMapsFX has been released to bintray and Maven Central.  The main feature in this version is to allow the use of custom marker/pin images, rather than relying on the default Google images.

A future blog post will demonstrate how to add custom markers to your GMapsFX application.

 

Screen Shot 2016-05-20 at 2.25.33 PM

 

GMapsFX 2.0.6 Released

A new version of GMapsFX has been released to bintray and Maven Central which contains

  • additional bug fixes related to hiding/showing the directions pane at runtime.
  • Ability to pass the map/directions language to the Google map at runtime, eliminating the need to hardcode it.

Screen Shot 2016-05-20 at 2.25.33 PM.png

<dependency> 
  <groupId>com.lynden</groupId> 
  <artifactId>GMapsFX</artifactId> 
  <version>2.0.6</version> 
</dependency>

 

GMapsFX Home: http://rterp.github.io/GMapsFX/
twitter: @RobTerpilowski

GMapsFX 2.0.5 Released

GMapsFX 2.0.5 has been released and is now available via bintray and maven central.

This version contains a major bugfix where the directions pane was always enabled.  The framework has been updated to make the direction pane an option that can be toggled on or off at runtime as it is needed.

GMapsFX is a Java library that makes it extremely easy to add a Google Map to a JavaFX application.

 

<dependency> 
  <groupId>com.lynden</groupId> 
  <artifactId>GMapsFX</artifactId> 
  <version>2.0.5</version> 
  <type>pom</type> 
</dependency>

 

GMaps Home: http://rterp.github.io/GMapsFX/
twitter: @RobTerpilowski

Use JavaFX to Add Google Maps to your NetBeans RCP Application

By utilizing the GMapsFX library which provides a JavaFX API for Google Maps, it is relatively straightforward to add a map component to a desktop application built on the NetBeans Rich Client Platform (RCP).

Assume we would like to add a map to a new TopComponent in the Editor mode in the frame, or for those who are not as familiar with NetBeans jargon, we would like to add a Map to a new tab within the main portion of the application frame.

First create a new NetBeans Module project which will utilize the GMapsFX component.

enter image description here

Enter project details
enter image description here

Use RELEASE80 of the NetBeans Platform. Please also note that the application will require Java 8 in order to run.
enter image description here

Once the project has been created, add the GMapsFX library as a dependency to the project.
The binaries are available on Maven Central and the source is available at GitHub: https://github.com/rterp/GMapsFX
enter image description here

Once the dependency has been added, right click on the project in NetBeans and select “New” -> “Other”. Select the “Module Development” category and then select “Window” file type. This will create a new TopComponent class which will be used to host the map component.

 

enter image description here

Enter a prefix for the new TopComponent class.
enter image description here

Once this is finished a new GMapTopCompoent class will be created. The GoogleMapView component can then be added to this class in order to display the map.

Below is the code for the entire TopCompoment class including code in which I added the map component as well as a couple of map markers and an info window, all without having to interact with the underlying Google Maps JavaScript API.

package com.lynden.gmapsfx.module;

import com.lynden.gmapsfx.GoogleMapView;
import com.lynden.gmapsfx.MapComponentInitializedListener;
import com.lynden.gmapsfx.javascript.object.Animation;
import com.lynden.gmapsfx.javascript.object.GoogleMap;
import com.lynden.gmapsfx.javascript.object.InfoWindow;
import com.lynden.gmapsfx.javascript.object.InfoWindowOptions;
import com.lynden.gmapsfx.javascript.object.LatLong;
import com.lynden.gmapsfx.javascript.object.MapOptions;
import com.lynden.gmapsfx.javascript.object.MapTypeIdEnum;
import com.lynden.gmapsfx.javascript.object.Marker;
import com.lynden.gmapsfx.javascript.object.MarkerOptions;
import java.awt.BorderLayout;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import org.netbeans.api.settings.ConvertAsProperties;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.windows.TopComponent;
import org.openide.util.NbBundle.Messages;

/**
 * Top component which displays something.
 */
@ConvertAsProperties(
        dtd = "-//com.lynden.gmapsfx.module//GMap//EN",
        autostore = false
)
@TopComponent.Description(
        preferredID = "GMapTopComponent",
        //iconBase="SET/PATH/TO/ICON/HERE", 
        persistenceType = TopComponent.PERSISTENCE_ALWAYS
)
@TopComponent.Registration(mode = "editor", openAtStartup = true)
@ActionID(category = "Window", id = "com.lynden.gmapsfx.module.GMapTopComponent")
@ActionReference(path = "Menu/Window" /*, position = 333 */)
@TopComponent.OpenActionRegistration(
        displayName = "#CTL_GMapAction",
        preferredID = "GMapTopComponent"
)
@Messages({
    "CTL_GMapAction=GMap",
    "CTL_GMapTopComponent=GMap Window",
    "HINT_GMapTopComponent=This is a GMap window"
})


public final class GMapTopComponent extends TopComponent implements MapComponentInitializedListener {

    protected GoogleMapView mapComponent;
protected GoogleMap map;

public GMapTopComponent() {
    initComponents();
    setName(Bundle.CTL_GMapTopComponent());
    setToolTipText(Bundle.HINT_GMapTopComponent());
    setLayout(new BorderLayout());
    JFXPanel panel = new JFXPanel();
    Platform.setImplicitExit(false);

    Platform.runLater(() -> {
        mapComponent = new GoogleMapView();
        mapComponent.addMapInializedListener(this);
        BorderPane root = new BorderPane(mapComponent);
        Scene scene = new Scene(root);
        panel.setScene(scene);
    });

        add(panel, BorderLayout.CENTER);        

    }


  @Override
    public void mapInitialized() {
        //Once the map has been loaded by the Webview, initialize the map details.
        LatLong center = new LatLong(47.606189, -122.335842);



MapOptions options = new MapOptions();
        options.center(center)
                .mapMarker(true)
                .zoom(9)
                .overviewMapControl(false)
                .panControl(false)
                .rotateControl(false)
                .scaleControl(false)
                .streetViewControl(false)
                .zoomControl(false)
                .mapType(MapTypeIdEnum.ROADMAP);

        map = mapComponent.createMap(options);

        //Add a couple of markers to the map.
        MarkerOptions markerOptions = new MarkerOptions();
        LatLong markerLatLong = new LatLong(47.606189, -122.335842);
        markerOptions.position(markerLatLong)
                .title("My new Marker")
                .animation(Animation.DROP)
                .visible(true);

        Marker myMarker = new Marker(markerOptions);

        MarkerOptions markerOptions2 = new MarkerOptions();
        LatLong markerLatLong2 = new LatLong(47.906189, -122.335842);
        markerOptions2.position(markerLatLong2)
                .title("My new Marker")
                .visible(true);

        Marker myMarker2 = new Marker(markerOptions2);

        map.addMarker(myMarker);
        map.addMarker(myMarker2);

        //Add an info window to the Map.
        InfoWindowOptions infoOptions = new InfoWindowOptions();
        infoOptions.content("<h2>Center of the Universe</h2>")
                .position(center);

        InfoWindow window = new InfoWindow(infoOptions);
        window.open(map, myMarker);

    }    

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 400, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 300, Short.MAX_VALUE)
        );
    }// </editor-fold>                        

    // Variables declaration - do not modify                     
    // End of variables declaration                   
    @Override
    public void componentOpened() {
        // TODO add custom code on component opening
    }

    @Override
    public void componentClosed() {
        // TODO add custom code on component closing
    }

    void writeProperties(java.util.Properties p) {
        // better to version settings since initial version as advocated at
        // http://wiki.apidesign.org/wiki/PropertyFiles
        p.setProperty("version", "1.0");
        // TODO store your settings
    }

    void readProperties(java.util.Properties p) {
        String version = p.getProperty("version");
        // TODO read your settings according to their version


       }
    }

What is important to note is that no map related classes can be instantiated until the GoogleMapView has been initialized. This is because the underlying JavaScript peer objects can’t be created until the JavaScript runtime has been initialized. The TopComponent is added as a MapComponentInitializedListener so that it can determine when it is safe to begin manipulating the map and its associated objects.

This module is now ready to be included as a dependency in a NetBeans application in development, or be added as a plug-in to an existing Netbeans RCP application at runtime, or even added to the IDE itself.

One word of caution: The underlying JavaFX WebView and Javascript runtime which the GoogleMapView component is making use of to render the map appears to be a memory hog. I have had to play with memory settings in order to avoid OutOfMemoryErrors, so something to keep in mind as you play with this.

Below is the final product of running the GMapsFX plug-in within a NetBeans RCP application.

enter image description here

The GMapsFX project is open source with the project home at GitHub as mentioned above and can be accessed at:
http://rterp.github.io/GMapsFX/

twitter: @RobTerp

GMapsFX Version 1.1.0

GMapsFX 1.1.0 has been released and supports the following new features and bug fixes.

We are at also looking at breaking the Java-to-JavaScript abstraction layer out of this project and creating its own project for it, as this layer could prove useful for interacting with other JavaScript applications within a JavaFX WebView.

We will have project artifacts published to Maven Central in the very near future.

The latest GMapsFX.jar file can be found here

Project documentation can be found here and with Javadocs located here

GMapsFX Version 1.0.0

GMapsFX 1.0.0 has been released and supports the following features.

  • Markers
  • Marker Animations
  • Info Windows
  • Polylines
  • Shapes
    • Circles
    • Polygons
    • Rectangles
  • Map State Events
    • Bounds Changed
    • Center Changed
    • Drag
    • Drag End
    • Drag Start
    • Heading Changed
    • Idle
    • MapTypeId Changed
    • Projection Changed
    • Resize
    • Tiles Loaded
    • Tilt Changed
    • Zoom Changed
  • Map UI Events
    • Click
    • Double Click
    • Mouse Move
    • Mouse Up
    • Mouse Down
    • Mouse Over
    • Mouse Out
    • Right Click

Below is a sample map displaying some of the new features including markers, a polyline, an info window, a rectangle, and a circle, as well as events which are monitoring the lat/long position of the center of the map as it is dragged around and updates the value at the top of the UI.

 

enter image description here

Special thanks to Geoff Capper who contributed the shape and event handling code for the latest version.

The GMapsFX.jar file can be found here
Project documentation can be found here and with Javadocs located here

Written with StackEdit.

GMapsFX :: Add Google Maps to your JavaFX application.

We have been considering adding a map component to our Freight Management application built on the NetBeans RCP and JavaFX, which would allow Lynden dispatchers to track its drivers throughout the city as well as highlight where trailers have been dropped off at customer locations and are ready for pickup.

Google Maps is a logical tool which could be utilized to accomplish this task. While there are examples out on the web for integrating Google Maps with JavaFX, these solutions require mingling JavaScript within the Java code in order to interact with a Google Map loaded within the application.

In an effort to remove the need to code JavaScript within JavaFX in order to use Google Maps, I have created a Java API ‘wrapper’ around the Google Maps JavaScript API and have dubbed this framework GMapsFX. This allows one to add a Google Map component to a JavaFX application and interact with it utilizing a pure Java API.

While at the present time only the most basic Google Map functionality has been ‘wrapped’ by the Java API, I am making this project open source in the hopes that if others find this library useful and require additional functionality, that it could be added and contributed back to the community.

The project can be found on GitHub at:
http://rterp.github.io/GMapsFX/

with the JavaDocs available at:
http://rterp.github.io/GMapsFX/apidocs/

Below is an example of using GMapsFX to add a map component to a Scene, setting the location to Seattle, and then adding a Marker to the map.

package com.lynden.gmapsexampleapp;

import com.lynden.gmapsfx.GoogleMapView;
import com.lynden.gmapsfx.MapComponentInitializedListener;
import com.lynden.gmapsfx.javascript.object.GoogleMap;
import com.lynden.gmapsfx.javascript.object.LatLong;
import com.lynden.gmapsfx.javascript.object.MapOptions;
import com.lynden.gmapsfx.javascript.object.MapType;
import com.lynden.gmapsfx.javascript.object.Marker;
import com.lynden.gmapsfx.javascript.object.MarkerOptions;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.scene.Scene;
import javafx.stage.Stage;


public class MainApp extends Application implements MapComponentInitializedListener {

GoogleMapView mapView;
GoogleMap map;

@Override
public void start(Stage stage) throws Exception {

    //Create the JavaFX component and set this as a listener so we know when 
    //the map has been initialized, at which point we can then begin manipulating it.
    mapView = new GoogleMapView();
    mapView.addMapInializedListener(this);

    Scene scene = new Scene(mapView);

    stage.setTitle("JavaFX and Google Maps");
    stage.setScene(scene);
    stage.show();
}


@Override
public void mapInitialized() {
    //Set the initial properties of the map.
    MapOptions mapOptions = new MapOptions();

    mapOptions.center(new LatLong(47.6097, -122.3331))
            .mapType(MapType.ROADMAP)
            .overviewMapControl(false)
            .panControl(false)
            .rotateControl(false)
            .scaleControl(false)
            .streetViewControl(false)
            .zoomControl(false)
            .zoom(12);

    map = mapView.createMap(mapOptions);

    //Add a marker to the map
    MarkerOptions markerOptions = new MarkerOptions();

    markerOptions.position( new LatLong(47.6, -122.3) )
                .visible(Boolean.TRUE)
                .title("My Marker");

    Marker marker = new Marker( markerOptions );

    map.addMarker(marker);

}

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

 


 

The code above produces the following result

enter image description here

 

twitter: @RobTerp

Written with StackEdit.