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.

54 thoughts on “GMapsFX :: Add Google Maps to your JavaFX application.

  1. Pingback: JavaFX links of the week, May 5 // JavaFX News, Demos and Insight // FX Experience

  2. Pingback: Java desktop links of the week, May 5 « Jonathan Giles

    • Good question. I believe this is possible to do with the Google JavaScript API, so it would just be a matter of implementing the required JavaScript functions in the GMapsFX library to support this.

      I can add this to the enhancement list if you like.

  3. What do i have to keep in mind with the Google API restrictions from 2500 requests per day?
    How many requests are produced by the library? All in all a great library that has all that i need currently

    • Hi Clayn,
      The library is basically a WebView wrapper around the Javascript API, so it is not making any additional calls than if you were running the Google Maps API in a browser based application with Javascript.

      hth,
      -Rob

  4. Provided code sample issues the following stack trace :
    netscape.javascript.JSException: ReferenceError: Can’t find variable: google
    at com.sun.webkit.dom.JSObject.fwkMakeException(JSObject.java:128)
    at com.sun.webkit.WebPage.twkExecuteScript(Native Method)
    at com.sun.webkit.WebPage.executeScript(WebPage.java:1410)
    at javafx.scene.web.WebEngine.executeScript(WebEngine.java:934)
    at com.lynden.gmapsfx.javascript.JavaFxWebEngine.executeScript(JavaFxWebEngine.java:39)
    at com.lynden.gmapsfx.javascript.JavascriptRuntime.execute(JavascriptRuntime.java:64)
    at com.lynden.gmapsfx.javascript.JavascriptObject.(JavascriptObject.java:49)
    at com.lynden.gmapsfx.javascript.object.LatLong.(LatLong.java:36)
    at application.Sample.mapInitialized(Sample.java:45)
    at com.lynden.gmapsfx.GoogleMapView.fireMapInitializedListeners(GoogleMapView.java:132)
    at com.lynden.gmapsfx.GoogleMapView$1.changed(GoogleMapView.java:74)
    at com.lynden.gmapsfx.GoogleMapView$1.changed(GoogleMapView.java:61)
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:176)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
    at javafx.beans.property.ReadOnlyObjectWrapper$ReadOnlyPropertyImpl.fireValueChangedEvent(ReadOnlyObjectWrapper.java:176)
    at javafx.beans.property.ReadOnlyObjectWrapper.fireValueChangedEvent(ReadOnlyObjectWrapper.java:142)
    at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:112)
    at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:145)
    at javafx.scene.web.WebEngine$LoadWorker.updateState(WebEngine.java:1214)
    at javafx.scene.web.WebEngine$LoadWorker.dispatchLoadEvent(WebEngine.java:1325)
    at javafx.scene.web.WebEngine$LoadWorker.access$1100(WebEngine.java:1207)
    at javafx.scene.web.WebEngine$PageLoadListener.dispatchLoadEvent(WebEngine.java:1194)
    at com.sun.webkit.WebPage.fireLoadEvent(WebPage.java:2373)
    at com.sun.webkit.WebPage.fwkFireLoadEvent(WebPage.java:2217)
    at com.sun.webkit.network.URLLoader.twkDidFail(Native Method)
    at com.sun.webkit.network.URLLoader.notifyDidFail(URLLoader.java:842)
    at com.sun.webkit.network.URLLoader.access$1300(URLLoader.java:43)
    at com.sun.webkit.network.URLLoader$7.run(URLLoader.java:824)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.access$300(WinApplication.java:39)
    at com.sun.glass.ui.win.WinApplication$4$1.run(WinApplication.java:112)
    at java.lang.Thread.run(Thread.java:745)

    What’s the matter ?

      • The problem comes with the line 114 in the MainApp sample:
        LatLong center = new LatLong(47.606189, -122.335842);
        This line executes the following javascript :
        var LatLong0 = new google.maps.LatLng(47.606189,-122.335842)

        OS = Windows 7

    • Got this exception several times. Figured out it happens when i try to use something that uses the webeninge (in my cases for example “new LatLng()”) when the GoogleMapView wasnt initialized yet. So i think you should wait for such things until “mapInitialized” was called.
      Maybe this works for you

      • Yes, that is exactly what the example is doing? The LatLong objects are being initialized in the mapInitialized callback method. The strange thing is that I can’t seem to replicate this issue. I’m downloading Eclipse now, to see if I can replicate it there, but I’ve tried the example app on a few different machines with different OSs, but haven’t had any luck.

    • Hello, Guys

      I had this same issue, but i solved this in another away. I don’t use in FXML file. I use a AnchorPane and add my GoogleMapView object in this. For example:

      FXML File:

      Controller:

      @FXML
      private AnchorPane GmapAnchorPanel;
      private GoogleMapView mapView;


      GmapAnchorPanel.getChildren().add(mapView);

      Best regards,

  5. I try the sample code on this page (just changing MapType to MapTypeIdEnum) on Eclipse Luna (4.4.0M6) and JDK 1.8.0_05-b13.
    The same exception with version 1.1.0 and 1.1.1.

    • Are you able to run the example app bundled in the release build of 1.1.1?
      java -jar GMapsFX-1.1.1.jar

      The sample code doesn’t start manipulating the Map objects until the underlying map view is created so I’m not sure how the exception is being thrown.

      I’ve tried this out on a couple of different Win 7 and Ubuntu machines, and unfortunately can’t replicate this.

      • Hi! I have received the same error as Jean-Pascal (netscape.javascript.JSException: ReferenceError: Can’t find variable: google), i have tried it with GMapsFX-1.1.1.jar y con GMapsFX-1.1.0.jar but i haven’t get nothing.
        I’ll agree you alot if you’d say us how to solve it.
        Thank you very much.

  6. Well, I got it but without any proxy in my connection. Can rterp inform me if it exists any solutions to use this solution througth a proxy connection?
    Thank you very much!

    • You’ll need to set the following system properties, either in your class that launches the application, or on the command line.

      “http.proxyHost”
      “http.proxyPort”

      • Thank you very much rterp, but if my proxy have user and password, what do i have to add? I have been trying but i have not got anything. :S

      • Try adding the following system properties:
        System.getProperties().put(“http.proxyUser”, “someUserName”);
        System.getProperties().put(“http.proxyPassword”, “somePassword”);

      • In my environment I also need

        “https.proxyHost”
        “https.proxyPort”

        (usually, the same values as for the the http-case)

    • Unfortunately since the JavaFX WebView is pulling down the image tiles in real-time I don’t think there’s an easy way to cache the map.

      • Thats a “nice” answer. I thought the part that makes the hole thing impossible is the javascript part

  7. Im getting this exception on the provided source demo:

    netscape.javascript.JSException: RangeError: Maximum call stack size exceeded.
    at com.sun.webkit.dom.JSObject.fwkMakeException(JSObject.java:128)
    at com.sun.webkit.dom.JSObject.fwkMakeException(JSObject.java:128)
    at com.sun.webkit.WebPage.twkExecuteScript(Native Method)
    at com.sun.webkit.WebPage.executeScript(WebPage.java:1410)
    at javafx.scene.web.WebEngine.executeScript(WebEngine.java:934)
    at com.lynden.gmapsfx.javascript.JavaFxWebEngine.executeScript(JavaFxWebEngine.java:39)
    at com.lynden.gmapsfx.GoogleMapView.mapResized(GoogleMapView.java:101)
    at com.lynden.gmapsfx.GoogleMapView.lambda$createMap$19(GoogleMapView.java:127)
    at com.lynden.gmapsfx.GoogleMapView$$Lambda$6/33095720.handle(Unknown Source)
    at com.lynden.gmapsfx.javascript.event.EventHandlers.handleStateEvent(EventHandlers.java:107)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:71)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:275)
    at com.sun.webkit.Utilities$1.run(Utilities.java:75)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.webkit.Utilities.fwkInvokeWithContext(Utilities.java:72)
    at com.sun.webkit.Timer.twkFireTimerEvent(Native Method)
    at com.sun.webkit.Timer.fireTimerEvent(Timer.java:66)
    at com.sun.webkit.Timer.notifyTick(Timer.java:47)
    at javafx.scene.web.WebEngine$PulseTimer$2.pulse(WebEngine.java:1154)
    at com.sun.javafx.tk.Toolkit$3.run(Toolkit.java:321)
    at com.sun.javafx.tk.Toolkit$3.run(Toolkit.java:319)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:319)
    at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:348)
    at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:479)
    at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:460)
    at com.sun.javafx.tk.quantum.QuantumToolkit$13.run(QuantumToolkit.java:327)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.access$300(WinApplication.java:39)
    at com.sun.glass.ui.win.WinApplication$4$1.run(WinApplication.java:112)
    at java.lang.Thread.run(Thread.java:745)

    I guess it is when trying to put the mark on the map cause it is not showing up

  8. I’m not be able to render images of the project in the icons MarkerOptions
    My code:

    markerOptions.icon( getClass().getClassLoader().getResource(“img/marker.png”).toString() );

    Can anyone?

    • I could not, I tried everything, absolute path, relative path. Only if I place image somewhere in the GMapsFX library – it works. The path should be relative to the maps.html file location.
      I could not find solution how to point application to icon next to my Jar or images/ folder for example

  9. Congratulations, nice project.

    I have a similar project using google maps with javafx and i´ve had a problem with the map, the map is too slow to use comparing with google map running in a browser, with zoom in/out, hybrid map, anything you need to do many times like zoom in/out. I donwloaded this project and realized it´s occured too, searching on web i found a post saying that it occurs because javafx webview doesn´t have cache like a browser, is it true ? is there any way to solve this problem ?

  10. Hello, i used the map it works perfectly, the only matter is that i have to put in in a tabpane in my project and i can’t know how to do it so please if you can help and thanks 😀

  11. The line with the error:

    ————————————
    EventHandlers.java
    ————————————

    public void handleStateEvent(String callbackKey) {
    if (handlers.containsKey(callbackKey) && handlers.get(callbackKey) instanceof StateEventHandler) {
    ((StateEventHandler) handlers.get(callbackKey)).handle();
    } else {
    System.err.println(“Error in handle: ” + callbackKey + ” for state handler “);
    }
    }

    The code: “((StateEventHandler) handlers.get(callbackKey)).handle();”

    return the following message:

    ” ((StateEventHandler) handlers.get(callbackKey)).handle() = >Exception occurred in target VM: RangeError: Maximum call stack size exceeded.” …

    Thanks

    Javier

    • Hi Regula, I was able to do this in the example by putting the buttons in a GridPane and putting the GridPane on top of the Pane that contained the Map View component.

      hth,
      -Rob

  12. Hi, Rob, how are you?

    I’m exploring your project and i hava a question: it’s possible to take a click event in a marker? For example:
    myMarker.addStateEventHandler(MarkerEventType.select, () -> {
    System.out.println(“Latitude” + myMarker.getMarkerOptions().getLatLong().getLatitude().);
    });

    Best Regards,

    • Very late but just incase you do read this

      map.addUIEventHandler(poly, UIEventType.click, (JSObject obj) -> {
      LatLong ll = new LatLong((JSObject) obj.getMember(“latLng”));
      System.out.println(“You clicked the line at LatLong: lat: ” + ll.getLatitude() + ” lng: ” + ll.getLongitude());
      });

      this is from his main app example, poly is a Polyline, I have been able to replicate it using a Marker in the place of the Polyline

      • hi, How are u ?
        I want to know if it’s possible to draw a polyline on GoogleMap ??
        Best Regards,

  13. Hi,
    Thanks for the great work!
    I have only one suggestion in regards to this tutorial:
    I think “MapType” should be changed to “MapTypeIdEnum” for it to work in the current version though.

  14. Hi,

    Congratulations, this is a great library.

    Some suggestion: Is it possible to add a closeclick-UI-Event (especially for the InfoWindow) ?

  15. Where do I put in my Google API key? When I run this, I get
    Caused by: java.lang.IllegalStateException: Couldn’t load map file ‘/html/maps.html’: NullPointerException
    at com.lynden.gmapsfx.GoogleMapView.(GoogleMapView.java:162)
    What goes in this file?

Leave a Reply to rterpCancel reply