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 project details
Use RELEASE80 of the NetBeans Platform. Please also note that the application will require Java 8 in order to run.
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
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 a prefix for the new TopComponent class.
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.
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
I am getting javascript error. Map is show but markers are not shown, please help.
I have included version 1.1.1 src in my project.
Regards
asutosh
========
GoogleMapView.mapResized: triggering resize event
netscape.javascript.JSException: RangeError: Maximum call stack size exceeded.
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$5(GoogleMapView.java:127)
at com.lynden.gmapsfx.GoogleMapView$$Lambda$7/5052518.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.GeneratedMethodAccessor9.invoke(Unknown Source)
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:322)
at com.sun.javafx.tk.Toolkit$3.run(Toolkit.java:320)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:320)
at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:349)
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:744)
Hi Asutosh,
if you could post your project somewhere, I’d be happy to take a look.
-Rob
Pingback: JavaFX links of the week, June 9 // JavaFX News, Demos and Insight // FX Experience
Pingback: Java desktop links of the week, June 9 « Jonathan Giles
Nice, Rob. Looking forward to trying out GMapsFX with a NB RCP application. Thanks for posting. -Gail
Thanks Gail!
If i have release74 will it work?
Thankyou
hmmm, that I am not sure about. I think you need to be using at least 80, but you can give it a try.
The only available version for Java is Dev-SNAPSHOT (in NB 8.1), and then the module cannot find its libraries.
Could not transfer metadata org.netbeans.api:org-netbeans-api-annotations-common:dev-SNAPSHOT/maven-metadata.xml from/to netbeans-snapshot (http://bits.netbeans.org/nexus/content/repositories/snapshots): Failed to transfer file: http://bits.netbeans.org/nexus/content/repositories/snapshots/org/netbeans/api/org-netbeans-api-annotations-common/dev-SNAPSHOT/maven-metadata.xml. Return code is: 502 , ReasonPhrase:Bad Gateway.
Failure to transfer org.netbeans.api:org-netbeans-api-annotations-common:dev-SNAPSHOT/maven-metadata.xml from http://bits.netbeans.org/nexus/content/repositories/snapshots was cached in the local repository, resolution will not be reattempted until the update interval of netbeans-snapshot has elapsed or updates are forced. Original error: Could not transfer metadata org.netbeans.api:org-netbeans-api-annotations-common:dev-SNAPSHOT/maven-metadata.xml from/to netbeans-snapshot (http://bits.netbeans.org/nexus/content/repositories/snapshots): Failed to transfer file: http://bits.netbeans.org/nexus/content/repositories/snapshots/org/netbeans/api/org-netbeans-api-annotations-common/dev-SNAPSHOT/maven-metadata.xml. Return code is: 502 , ReasonPhrase:Bad Gateway.
Downloading: http://bits.netbeans.org/nexus/content/repositories/snapshots/org/netbeans/api/org-netbeans-api-annotations-common/dev-SNAPSHOT/org-netbeans-api-annotations-common-dev-SNAPSHOT.pom
Does this happen when building the project or when attempting to run a GMap plugin in NetBeans?
This happens trying to follow the tutorial Rob posted. I cannot build the project. Try it.
Hey, thanks for the help. I’m graduating in Computer Science, and my final project is supposed to use Google Maps API with JavaFx… Ayway, i just did everything that you said, but this part of the code displays error:
Platform.runLater(() -> {
mapComponent = new GoogleMapView();
mapComponent.addMapInializedListener(this);
BorderPane root = new BorderPane(mapComponent);
Scene scene = new Scene(root);
panel.setScene(scene);
});
The error I got is this one: illegal start of expression
Do you have any clue of what it can be?
Is this a compile time error or a runtime error?
It’s a compile time error and I have been struggling with it for a while now
Platform.runLater(() ->
{mapComponent = new GoogleMapView();
mapComponent.addMapInializedListener(this);
BorderPane root = new BorderPane(mapComponent);
Scene scene = new Scene(root);
panel.setScene(scene);
});
The error I got is this one: lambda expression not expected here
lambda expressions are not supported in -source 1.6 and I am already using the 1.8jdk.
Would appreciate if you responded as soon as you can since I have to finish this project today
What IDE are you using? It sounds like your project has been configured to use version 1.6 source, but GMapsFX requires 1.8.
I’d also like instructions on how to make this plot appear in a jPanel or a JFrame. I did get GMapsFX running as an application.
I updated all Netbeans references in the pom file to RELEASE81 and it is able to build and run.
Still some issues:
The POM for org.netbeans.api:org-openide-nodes:jar:dev-SNAPSHOT is missing, no dependency information available
Downloading:
Downloading: http://bits.netbeans.org/nexus/content/repositories/snapshots/org/netbeans/api/org-netbeans-api-annotations-common/dev-SNAPSHOT/org-netbeans-api-annotations-common-dev-SNAPSHOT.pom
he POM for org.netbeans.api:org-netbeans-api-annotations-common:jar:dev-SNAPSHOT is missing, no dependency information available
Downloading: http://bits.netbeans.org/nexus/content/repositories/snapshots/org/netbeans/api/org-openide-util/dev-SNAPSHOT/org-openide-util-dev-SNAPSHOT.pom
The POM for org.netbeans.api:org-openide-util:jar:dev-SNAPSHOT is missing, no dependency information available
Downloading: http://bits.netbeans.org/nexus/content/repositories/snapshots/org/netbeans/api/org-openide-util-lookup/dev-SNAPSHOT/org-openide-util-lookup-dev-SNAPSHOT.pom
The POM for org.netbeans.api:org-openide-util-lookup:jar:dev-SNAPSHOT is missing, no dependency information available
Downloading: http://bits.netbeans.org/nexus/content/repositories/snapshots/org/netbeans/api/org-openide-nodes/dev-SNAPSHOT/org-openide-nodes-dev-SNAPSHOT.pom
The POM for org.netbeans.api:org-openide-nodes:jar:dev-SNAPSHOT is missing, no dependency information available
Downloading: http://bits.netbeans.org/nexus/content/repositories/snapshots/org/netbeans/api/org-netbeans-libs-asm/dev-SNAPSHOT/org-netbeans-libs-asm-dev-SNAPSHOT.pom
The POM for org.netbeans.api:org-netbeans-libs-asm:jar:dev-SNAPSHOT is missing, no dependency information available
Downloading: http://bits.netbeans.org/nexus/content/repositories/snapshots/org/netbeans/api/org-openide-actions/dev-SNAPSHOT/org-openide-actions-dev-SNAPSHOT.pom
The POM for org.netbeans.api:org-openide-actions:jar:dev-SNAPSHOT is missing, no dependency information available
Downloading: http://bits.netbeans.org/nexus/content/repositories/snapshots/org/netbeans/api/org-openide-dialogs/dev-SNAPSHOT/org-openide-dialogs-dev-SNAPSHOT.pom
The POM for org.netbeans.api:org-openide-dialogs:jar:dev-SNAPSHOT is missing, no dependency information available
Downloading: http://bits.netbeans.org/nexus/content/repositories/snapshots/org/netbeans/api/org-netbeans-swing-tabcontrol/dev-SNAPSHOT/org-netbeans-swing-tabcontrol-dev-SNAPSHOT.pom
The POM for org.netbeans.api:org-netbeans-swing-tabcontrol:jar:dev-SNAPSHOT is missing, no dependency information available
————————————————————————
Failed to execute goal on project GMapsFX-Module: Could not resolve dependencies for project com.lynden:GMapsFX-Module:nbm:1.0-SNAPSHOT: The following artifacts could not be resolved: org.netbeans.api:org-netbeans-api-annotations-common:jar:dev-SNAPSHOT, org.netbeans.api:org-openide-nodes:jar:dev-SNAPSHOT, org.netbeans.api:org-openide-util:jar:dev-SNAPSHOT, org.netbeans.api:org-openide-util-lookup:jar:dev-SNAPSHOT, org.netbeans.api:org-netbeans-libs-asm:jar:dev-SNAPSHOT, org.netbeans.api:org-openide-actions:jar:dev-SNAPSHOT, org.netbeans.api:org-openide-dialogs:jar:dev-SNAPSHOT, org.netbeans.api:org-netbeans-swing-tabcontrol:jar:dev-SNAPSHOT: Could not find artifact org.netbeans.api:org-netbeans-api-annotations-common:jar:dev-SNAPSHOT in netbeans-snapshot (http://bits.netbeans.org/nexus/content/repositories/snapshots) -> [Help 1]
James, can you post your POM somewhere so I can take a look?
https://www.dropbox.com/s/1rb5ky491y3otle/GxMaps.zip?dl=0
James, I updated all the dependencies from dev-SNAPSHOT to RELEASE81, and updated the compiler source and target from 1.6 to 1.8, and all seems fine now.
Modified POM is available at the link below.
https://gist.github.com/anonymous/dfb86faa93c2ecb97f968551978a4174
This *should* be doable. Its possible to embed a JavaFX component into a Swing container in Java 8, but I will need to do a bit of research to see how to get this to work.
I have never used a pom file before. I replaced the pom.xml file in my project with the above pom.xml, but still get
The POM for org.netbeans.api:org-netbeans-api-annotations-common:jar:RELEASE81 is missing, no dependency information available
The POM for org.netbeans.api:org-openide-windows:jar:RELEASE81 is missing, no dependency information available
The POM for org.netbeans.api:org-openide-util:jar:RELEASE81 is missing, no dependency information available
The POM for org.netbeans.api:org-openide-util-ui:jar:RELEASE81 is missing, no dependency information available
The POM for org.netbeans.api:org-openide-util-lookup:jar:RELEASE81 is missing, no dependency information available
The POM for org.netbeans.api:org-openide-awt:jar:RELEASE81 is missing, no dependency information available
The POM for org.netbeans.api:org-netbeans-modules-settings:jar:RELEASE81 is missing, no dependency information available
Do I need to do something else?
Thanks for the help
hey… i have a problem when load that infoWindow. My application get stuck if i load that infoWindow. but, if i don’t add that, my application is okay. I use same procedure like what you give at infoWindow example..
may u would like to solve my problem..
thanks..