NetBeans Platform Tutorial for Geospatial Systems
This tutorial needs a review. You can edit it in GitHub following these contribution guidelines. |
The NetBeans Platform is a solid infrastructure for geospatial systems. It provides a docking framework with full screen mode, an application frame within which a map can be placed, and a loosely coupled module system for the organization of code into feature-oriented interdependent plugins.
In this tutorial, you are shown how to set up a typical geospatial application structure, leveraging the central features of the NetBeans Platform. You start by creating a new application, you add several modules, each representing a feature in your geospatial system, you configure the application to start up in full screen mode and hide tabs, while you integrate various mapping solutions.
By the end of this tutorial, you will have learned how to set up a powerful geospatial application infrastructure and configure the NetBeans Platform to optimize it for typical geospatial scenarios, while also learning how to integrate a variety of map technologies into your NetBeans Platform application, such as OpenMap, shown below:
For troubleshooting purposes, you are welcome to download the completed tutorial source code.
Creating the Application
We start by creating a new NetBeans Platform application as a starting point for our geospatial system.
-
Choose File | New Project and then choose NetBeans Modules. Select "NetBeans Platform Application", as shown below:
Click Next. Name your new application "MyGeospatialSystem" and specify a folder on disk for storing it:
Click Finish. The new project appears as follows in the Projects window:
-
Right-click the application and choose Run. The application deploys and you should see this:
Explore the menus and toolbars to see the available features that are part of your geospatial system out of the box.
You have learned how to create a new NetBeans Platform application, which is the starting point for your geospatial system.
Creating Features
Each feature in your application, such as a map feature or a country browser feature, will be contained within a NetBeans module. Modulerizing your features in this way lets you create multiple different distributions of your software, each consisting of a variety of different features provided by different modules. We start by creating a new NetBeans module to contain a map feature.
-
Right-click the Modules node and choose Add New, as shown below:
-
Type Map and specify that the module should be stored within the application:
Click Next.
-
Type
org.geospatial.map
as the unique identifier for the module:
Click Finish. You should see that your application has a new module:
You now have the container for a new feature. Next, we’re going to add some JARs providing NetBeans APIs to the classpath of the module so that we can begin creating the starting point of our features.
-
Right-click the Libraries node and choose Add Module Dependency, as shown below:
Choose the following modules:
-
Lookup API
-
UI Utilities API
-
Utilities API
-
Window System API
Click OK and notice that the Libraries node now lists the new module dependencies you selected:
-
Right-click the
org.geospatial.map
and create a new Java class. Name itMyMapTopComponent.java
. Define it as follows:
package org.geospatial.map;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.TopComponent;
@TopComponent.Description(
preferredID = "MyMapTopComponent",
persistenceType = TopComponent.PERSISTENCE_ALWAYS)
@TopComponent.Registration(
mode = "editor",
openAtStartup = true)
@ActionID(
category = "Window",
id = "org.geospatial.map.MyMapTopComponent")
@ActionReferences({
@ActionReference(
path = "Menu/Window",
position = 0)
})
@TopComponent.OpenActionRegistration(
displayName = "#CTL_MyMapAction")
@Messages(
"CTL_MyMapAction=Open Map")
public class MyMapTopComponent extends TopComponent {
@Messages("CTL_MyMapName=Map")
public MyMapTopComponent() {
setDisplayName(Bundle.CTL_MyMapName());
}
}
-
Run the application again and notice that you now have a new window:
In the same way as you have now done, you can very easily create more new windows throughout your application.
You have the basis of your first feature. In the next section, you will integrate one or more map technologies into your feature.
Integrating Map Technologies
A variety of map technologies exist. In the subsections that follow, you will learn how to integrate a range of different map technologies into your NetBeans Platform application. Each of the scenarios below are alternatives, each building on top of the previous sections of this tutorial.
Scenario 1: OpenMap
OpenMap is an open-source map technology.
-
Download OpenMap: http://openmap.bbn.com/cgi-bin/license.cgi
1. Right-click the application’s Modules node, choose Add New Library:
Then create a library wrapper module that wraps the OpenMap JAR files. Name the module OpenMapLib and use org.openmap
as the code name base. When you complete the wizard, you should see your new module, with the Libraries node listing the OpenMap JARs:
Next, set a dependency on the OpenMapLib module after right-clicking the Map module’s Libraries node and choosing Add Module Dependency, as shown below:
You should notice that the OpenMapLib module is now a dependency in your Map module, so that you can use the OpenMap JARs in your Map module:
-
In the MapTopComponent, use OpenMap as follows, as a first example to get started with your OpenMap/NetBeans Platform integration:
public class MyMapTopComponent extends TopComponent {
private final InstanceContent ic = new InstanceContent();
@Messages("CTL_MyMapName=Map")
public MyMapTopComponent() {
setDisplayName(Bundle.CTL_MyMapName());
setLayout(new BorderLayout());
try {
//MapPanel:
MapPanel mapPanel = new BasicMapPanel();
//MapHandler:
MapHandler mapHandler = mapPanel.getMapHandler();
mapHandler.add(new LayerHandler());
mapHandler.add(this);
//MapBean:
MapBean mapBean = mapPanel.getMapBean();
mapBean.setScale(120000000f);
//Selection:
MouseDelegator mouseDelegator = new MouseDelegator();
mapHandler.add(mouseDelegator);
SelectMouseMode selectMouseMode = new SelectMouseMode();
mapHandler.add(selectMouseMode);
mouseDelegator.setActive(selectMouseMode);
//MapMouseListener:
final MyMapMouseListener myMapMouseListener = new MyMapMouseListener();
//ShapeLayer:
ShapeLayer shapeLayer = new ShapeLayer() {
@Override
public synchronized MapMouseListener getMapMouseListener() {
return myMapMouseListener;
}
};
//Properties:
Properties shapeLayerProps = new Properties();
shapeLayerProps.put("lineColor", "000000");
shapeLayerProps.put("fillColor", "BDDE83");
shapeLayerProps.put("shapeFile", "org/geospatial/map/dcwpo-browse.shp");
shapeLayerProps.put("spatialIndex", "org/geospatial/map/dcwpo-browse.ssx");
//Assign properties to ShapeLayer:
shapeLayer.setProperties(shapeLayerProps);
shapeLayer.setVisible(true);
//Assign ShapeLayer to MapHandler:
mapHandler.add(shapeLayer);
add(mapPanel.getMapBean(), BorderLayout.CENTER);
} catch (MultipleSoloMapComponentException msmce) {
}
associateLookup(new AbstractLookup(ic));
}
public class MyMapMouseListener implements MapMouseListener {
@Override
public String[] getMouseModeServiceList() {
return new String[]{SelectMouseMode.modeID};
}
@Override
public boolean mouseClicked(MouseEvent e) {
MapMouseEvent mme = (MapMouseEvent) e;
//Optionally:
//LatLonPoint latLonPoint = LatLonPoint.getFloat(mme.getLatLon());
//UTMPoint utmPoint = UTMPoint.LLtoUTM(latLonPoint);
//Publish something into Lookup:
ic.set(Collections.singleton(mme.getLatLon()), null);
StatusDisplayer.getDefault().setStatusText(mme.getLatLon().toString());
return true;
}
@Override
public boolean mousePressed(MouseEvent e) {return true;}
@Override
public boolean mouseReleased(MouseEvent e) {return true;}
@Override
public void mouseEntered(MouseEvent e) {}
@Override
public void mouseExited(MouseEvent e) {}
@Override
public boolean mouseDragged(MouseEvent e) {return true;}
@Override
public boolean mouseMoved(MouseEvent e) {return true;}
@Override
public void mouseMoved() {}
}
}
You should notice that you have the following import statements:
import com.bbn.openmap.LayerHandler;
import com.bbn.openmap.MapBean;
import com.bbn.openmap.MapHandler;
import com.bbn.openmap.MouseDelegator;
import com.bbn.openmap.MultipleSoloMapComponentException;
import com.bbn.openmap.event.MapMouseEvent;
import com.bbn.openmap.event.MapMouseListener;
import com.bbn.openmap.event.SelectMouseMode;
import com.bbn.openmap.gui.BasicMapPanel;
import com.bbn.openmap.gui.MapPanel;
import com.bbn.openmap.layer.shape.ShapeLayer;
import java.awt.BorderLayout;
import java.awt.event.MouseEvent;
import java.util.Collections;
import java.util.Properties;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.AbstractLookup;
import org.openide.util.lookup.InstanceContent;
import org.openide.windows.TopComponent;
From "share/data/shape" in the OpenMap distribution, copy dcwpo-browse.shp
and dcwpo-browse.ssx
into your module. In the source code above, make sure the references to the files point to the correct location in your module.
-
Run the application, click on the map, and you will be publishing new
Point2D
objects into theLookup
of theTopComponent
, while the status bar will also show new information whenever you click on the map:
Scenario 2: Geotoolkit
Geotoolkit is an open-source map technology.
-
Download Geotoolkit: http://www.geotoolkit.org/download.html
1. Right-click the application’s Modules node, choose Add New Library:
Then create a library wrapper module that wraps the GeoToolkit JAR file. Name the module GeoToolkit and use org.geotoolkit
as the code name base. When you complete the wizard, you should see your new module, with the Libraries node listing the GeoToolkit JAR:
Next, set a dependency on the OpenMapLib module after right-clicking the Map module’s Libraries node and choosing Add Module Dependency, as shown below:
You should notice that the OpenMapLib module is now a dependency in your Map module, so that you can use the OpenMap JARs in your Map module:
-
From the Geotoolkit distribution, copy
Countries.shp
file, as well as the DBF, PRJ, and SHX file into your module.
Alternatively, get these files here:
In the source code below, make sure the references to the files point to the correct location in your module.
-
In the MapTopComponent, use Geotoolkit as follows, as a first example to get started with your Geotoolkit/NetBeans Platform integration:
public class MyMapTopComponent extends TopComponent {
@Messages("CTL_MyMapName=Map")
public MyMapTopComponent() {
setDisplayName(Bundle.CTL_MyMapName());
setLayout(new BorderLayout());
MapContext mapContext = MapBuilder.createContext(DefaultGeographicCRS.SPHERE);
addShpData(mapContext);
JMap2D map = new JMap2D();
map.getContainer().setContext(mapContext);
map.setBackground(new Color(0, 150, 150));
JNavigationBar navBar = new JNavigationBar(map);
add(navBar, BorderLayout.NORTH);
add(map, BorderLayout.CENTER);
}
public void addShpData(MapContext context) {
try {
DataStore store = DataStoreFinder.getDataStore("url",
MyMapTopComponent.class.getResource("Countries.shp"));
Name name = store.getNames().iterator().next();
Session session = store.createSession(true);
FeatureCollection fs = session.getFeatureCollection(QueryBuilder.all(name));
MapLayer layer = MapBuilder.createFeatureLayer(fs,
RandomStyleFactory.createDefaultVectorStyle(fs));
layer.setVisible(true);
context.layers().add(layer);
} catch (DataStoreException e) {
}
}
}
The import statements are as follows: |
import java.awt.*;
import org.geotoolkit.data.*;
import org.geotoolkit.data.query.QueryBuilder;
import org.geotoolkit.gui.swing.go2.JMap2D;
import org.geotoolkit.gui.swing.go2.control.JNavigationBar;
import org.geotoolkit.map.*;
import org.geotoolkit.referencing.crs.DefaultGeographicCRS;
import org.geotoolkit.storage.DataStoreException;
import org.opengis.feature.type.Name;
import org.geotoolkit.util.RandomStyleFactory;
import org.geotoolkit.data.session.Session;
import org.openide.awt.*;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.TopComponent;
-
Run the application and you will see your Geotoolkit map component displayed in a window in your NetBeans Platform application.
Publishing and Subscribing to Objects of Interest
When a mouse click is performed in your map, you need to publish an object into the Lookup of the TopComponent. For example, you could publish the current Point on the map.
In the supporting windows, you need to implement a LookupListener. When the window opens, subscribe to the Lookup of the map window, while indicating that you want to be notified when a Point is published there. Whenever a new Point is made available, you can do something with it, for example, display it in the supporting window.
Conversely, you might need the map to be updated when one or more of the supporting windows change. In that case, the map window must be subscribed to the Lookup of the currently selected window or to the specific supporting window that it is interested in. The supporting window needs to published objects of interest to the map window.
The NetBeans Platform Quick Start describes this mechanism in detail.
Configuring the Geospatial System
In this section, you learn how to change a variety of default features of the NetBeans Platform to optimize them for usage in a geospatial environment.
All the configuration settings that follow are optional. Depending on your business needs, follow the instructions below to adapt your system to your needs.
Starting Up in Full Screen Mode
In this section, we start the application in full screen mode. Full screen mode is supported by default by the NetBeans Platform. It can be invoked by the user via View | Full Screen or by pressing Alt-Shift-Enter. However, in geospatial systems, you typically need to start the application in full screen mode automatically, so that the user will not need to take this step over and over again manually.
-
Create a new Java class named Installer.
-
Define the Installer class as follows:
package org.geospatial.map;
import org.openide.awt.Actions;
import org.openide.windows.OnShowing;
@OnShowing
public class Installer implements Runnable {
@Override
public void run() {
Actions.forID("Window", "org.netbeans.core.windows.actions.ToggleFullScreenAction").actionPerformed(null);
}
}
Run the application and notice that it starts in full screen mode.
Removing Tabs
In this section, we remove the tabs from all the windows in the application.
-
Set dependencies on the "Look & Feel Customization Library" and "Tab Control".
-
Create a class named
NoTabsTabDisplayerUI
, with this content
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import javax.swing.DefaultSingleSelectionModel;
import javax.swing.JComponent;
import javax.swing.SingleSelectionModel;
import javax.swing.plaf.ComponentUI;
import org.netbeans.swing.tabcontrol.TabDisplayer;
import org.netbeans.swing.tabcontrol.TabDisplayerUI;
public class NoTabsTabDisplayerUI extends TabDisplayerUI {
public NoTabsTabDisplayerUI(TabDisplayer displayer) {
super(displayer);
}
public static ComponentUI createUI(JComponent jc) {
assert jc instanceof TabDisplayer;
return new NoTabsTabDisplayerUI((TabDisplayer) jc);
}
private static final int[] PTS = new int[] { 0, 0, 0 };
@Override
public Polygon getExactTabIndication(int i) {
//Should never be called
return new Polygon(PTS, PTS, PTS.length);
}
@Override
public Polygon getInsertTabIndication(int i) {
return new Polygon(PTS, PTS, PTS.length);
}
@Override
public int tabForCoordinate(Point point) {
return -1;
}
@Override
public Rectangle getTabRect(int i, Rectangle rectangle) {
return new Rectangle(0,0,0,0);
}
@Override
protected SingleSelectionModel createSelectionModel() {
return new DefaultSingleSelectionModel();
}
public java.lang.String getCommandAtPoint(Point point) {
return null;
}
@Override
public int dropIndexOfPoint(Point point) {
return -1;
}
@Override
public void registerShortcuts(javax.swing.JComponent jComponent) {
//do nothing
}
@Override
public void unregisterShortcuts(javax.swing.JComponent jComponent) {
//do nothing
}
@Override
protected void requestAttention(int i) {
//do nothing
}
@Override
protected void cancelRequestAttention(int i) {
//do nothing
}
@Override
public Dimension getPreferredSize(javax.swing.JComponent c) {
return new Dimension(0, 0);
}
@Override
public Dimension getMinimumSize(javax.swing.JComponent c) {
return new Dimension(0, 0);
}
@Override
public Dimension getMaximumSize(javax.swing.JComponent c) {
return new Dimension(0, 0);
}
}
-
Add to the Installer created in the previous section the statements in bold below:
import javax.swing.UIManager;
import org.openide.awt.Actions;
import org.openide.windows.OnShowing;
@OnShowing
public class Installer implements Runnable {
@Override
public void run() {
Actions.forID("Window", "org.netbeans.core.windows.actions.ToggleFullScreenAction").actionPerformed(null);
*UIManager.put("ViewTabDisplayerUI", "org.geospatial.map.NoTabsTabDisplayerUI");
UIManager.put("EditorTabDisplayerUI", "org.geospatial.map.NoTabsTabDisplayerUI");*
}
}
Run the application and notice that the tabs are removed.
Congratulations! At this stage, with very little coding, you have created the starting point of an air-traffic control system.
To continue learning about the NetBeans Platform, head on to the four-part "NetBeans Platform Selection Management" series, which starts here. After that, get started with the NetBeans Platform Learning Trail, choosing the tutorials that are most relevant to your particular business scenario. Also, whenever you have questions about the NetBeans Platform, of any kind, feel free to write to the mailing list, dev@platform.netbeans.org; its related archive is here.
Have fun with the NetBeans Platform and see you on the mailing list!