[FrontPage] [TitleIndex] [WordIndex

Walkthrough: write a new iGeoDesktop module

This page explains how to write a simple module for iGeoDesktop.

1. Description

The goal of the module is to provide the user with a new menu entry, which shall be used to register a new tree (for some given feature type). The workflow will be like this:

2. Requirements

Readers of this page should be familiar with the Java programming language (in particular Swing/GUI programming), and be familiar with the basic structure of iGeoDesktop.

The complete source code of this example is available from the iGeoDesktop SVN, which can be found and checked out from our repository. The example module source code can be downloaded from here.

3. Class hierarchy

A typical module class will extend the DefaultModule class and overrides only the methods it needs to. This automatically takes care of loading and initializing the module etc. The DefaultModule class also takes a type parameter. The type parameter indicates the class of the application container. To make the module generic, you can just define your module to take that type parameter and pass it to the DefaultModule:

public class ExampleModule<T> extends DefaultModule<T> {
...
}

4. Configuration

We will configure our new module to define a new menu entry in the tools menu. This can be configured like this:

<dgp:ModuleRegister>
    <dgp:AbsolutePosition>
        <dgp:Window width="520" height="250" top="150" left="270"/>
    </dgp:AbsolutePosition>
    <dgp:Module>
        <dgp:name>example</dgp:name>
        <identifier>
            <value>example</value>
        </identifier>
        <dgp:className>org.deegree.igeo.modules.ExampleModule</dgp:className>
        <dgp:ViewForm>
            <dgp:PaneViewForm>
                <dgp:containerClass viewPlatform="Application">
                    org.deegree.igeo.views.swing.measure.MeasureFooterPanel
                </dgp:containerClass>
                <dgp:ComponentState modal="false" active="false">
                    <dgp:windowState>closed</dgp:windowState>
                </dgp:ComponentState>
                <dgp:paneTitle>$M1.PaneTitle</dgp:paneTitle>
            </dgp:PaneViewForm>
            <dgp:Layout>
                <dgp:NoneLayout/>
            </dgp:Layout>
        </dgp:ViewForm>
        <dgp:Menu>
            <identifier>
                <value>MeasureMB</value>
            </identifier>
            <dgp:parent>
                <value>MB1tools</value>
            </dgp:parent>
            <dgp:name/>
            <dgp:MenuItem>
                <dgp:name>Baum erfassen</dgp:name>
                <dgp:assignedAction>registerTree</dgp:assignedAction>
                <dgp:icon xlink:href="../icons/ruler.png"/>
            </dgp:MenuItem>
        </dgp:Menu>
    </dgp:Module>
</dgp:ModuleRegister>

The interesting part is the class definition <dgp:className>, which tells iGeoDesktop which class to instantiate, and the <dgp:assignedAction>registerTree</dgp:assignedAction>, telling iGeoDesktop which method to call when the user clicks our new menu item.

5. Method invocation

So we have to implement a method with the name of registerTree, and immediately show the user the information dialog. That's pretty easy:

/***/
public void registerTree() {
    // initial message
    DialogFactory.openInformationDialog( "application", ( (IGeoDesktop) appContainer ).getMainWndow(),
                                         "Please digitize a tree by clicking the corresponding location in the map.",
                                         "Information" );
    ...
}

The dialog factory is a utility class that can be used to display dialogs, you could instead use !JOptionPane or a custom dialog as well.

6. Interacting with other modules

After the user clicks ok, we need to activate the digitizing module, and select the function to digitize points. To do that, we first need to find the module. Information like this can be found via the application container. You can access the application container via the appContainer variable. So to find the digitizer module, you can loop through the module list:

    // find the digitizing module
    for ( IModule<?> m : appContainer.getModules() ) {
        if ( m instanceof DigitizerModule ) {
            DigitizerModule<?> dig = (DigitizerModule<?>) m;

            // open the digitize dialog
            dig.open();
            // select the drawPoint action
            dig.setDigitizingAction( "drawPoint" );
        }
    }

7. Interacting with the map(s)

In the next step, we have to register a listener with the selected layer, so we are informed once the user is finished digitizing. Access to the available map models (which usually correspond to maps which the user can see) is also done via the application container. Fetching the selected layer is pretty easy as well:

    // find selected layer
    MapModel mm = appContainer.getMapModelCollection().getMapModels().get( 0 );
    final Layer layer = mm.getLayersSelectedForAction( "action" ).get( 0 );

8. Adding a change listener

The change listener can be used to react to a variety of events. The event type which we are interested in, is the dataChanged event. So our change listener looks like this:

    // add a change listener to get events for the layer
    final ChangeListener listener = new ChangeListener() {
        public void valueChanged( ValueChangedEvent event ) {
            if ( event instanceof LayerChangedEvent ) {
                LayerChangedEvent e = (LayerChangedEvent) event;
                switch ( e.getChangeType() ) {
                // in case data changed, digitizing has finished
                case dataChanged:
                    // remove listener to prevent retrieving other unrelated events
                    layer.removeChangeListener( this );
                    // this is the newly digitized feature
                    Feature f = (Feature) e.getValue();
                    // this is the dialog where the rest of the information is obtained
                    new TreeDialog( layer, f ).setVisible( true );
                    break;
                case datasourceAdded:
                case datasourceChanged:
                case datasourceRemoved:
                case scaleRangeChanged:
                case featureSelected:
                case featureUnselected:
                case parentChanged:
                case visibilityChanged:
                case stylesSet:
                case styleAdded:
                case styleRemoved:
                case selectedForChanged:
                    break;
                }
            }
        }
    };

Once the event is fired, we need to remove the change listener (because we're not interested in subsequent events any more), and show the dialog where the properties are edited. The value associated with a dataChanged event is an instance of Feature, in our case the newly digitized feature. In case of the other events, nothing happens.

After the change listener is defined, it is added to the layer simply like this:

    layer.addChangeListener( listener );

9. Finishing up

The details of the GUI definition are not interesting here. Suppose the dialog defines a JTextField named treeType, where he has to enter the type of the tree. Then we can react to an ok click like this:

    // update the feature with the values in the dialog
    feature.setProperty( FeatureFactory.createFeatureProperty( new QualifiedName( "app", "NAME", APPNS ), treeType.getText() ), 0 );

Upon cancelling, the feature should be removed from the layer, and we can do that like this:

    // remove the feature
    ( (FeatureAdapter) layer.getDataAccess().get( 0 ) ).deleteFeature( feature );

10. Conclusion

The example only shows a fraction of the possibilities, and contains many simplifications. Of course, proper handling of multiple data sources, checking of the correct feature type in the selected layer, selecting the correct layer automatically (and setting it editable), automatic storing of the data in the data store, proper validation of user input etc. are all nice and important features, but they are beyond this simple tutorial.

Anyway, the example should give the interested developer a starting point from which to develop a custom module for iGeoDesktop.


CategoryDeegree2 CategoryHowTo


2018-04-20 12:04