Building our first plugin
- Approach
- The project creation wizard
- Project structure
- Dependencies between projects
- The Logic Library
- Presentation libray
- A test application
- Integrating with gvSIG
- Distributing our project.
Approach
At this point we will generate a project that will allow us to see a number of gvSIG's basic features. To do this we will create a small map viewer. The requirements might be:
- It must allow us to view a map of city blocks.
- There must be a query tool that allows us to click on a block to show information on the parcels associated with it.
- To customize the gvSIG splash and the background of the main window with the corporate image.
- When starting gvSIG display the map window and enable the cadastral information tool without the user having to interact with the application.
- Customise the "about" gvSIG to show corporate information.
- Disabled the editing tools on the map of city blocks.
- To prepare a plugin installation package so that users can install it using the Add-ons Manager.
To start making our project we will use the wizard to generate projects that are included in the gvSIG distribution.
The project creation wizard
The gvSIG distribution includes a plugin that displays an assistant for creating development projects on gvSIG. The extension providing this wizard is disabled by default so the first thing we must do is to activate it.
Tip
To assist you in the creation of development projects you can find documentation in the Creating our project section of gvSIG's Guide for developers.
To do this, go to the gvSIG preferences panel, and in General, Extensions look for "org.gvsig.mkmvnproject.MakeMavenProjectExtension", and activate the extension. You will need to restart gvSIG to effect the change.
After restarting gvSIG you will notice that the "Tools" menu now has a "Development" sub-menu, which contains a Create new plugin option.
It is important that we supply the following information properly:
Project name. The name you're going to give the project, following the normal rules for a Java identifier, trying not to use underscores, and capitalising when consisting of more than one word. We will use Viewer for this example.
GroupId. We normally use the java package name that is used as the base in our organization. In gvSIG we use org.gvsig so that is what we will use in this example.
Note
The wizard produces an error when a GroupId other than org.gvsig is used. We will try to fix this soon.
Path where the workspace is created. Use /home/gvSIG/workspace/viewer.
Having entered this data, select the Basic plugin with spatial support option.
Click Next to run the script and wait for it to finish generating the project. This can take several minutes depending on your Internet connection and your computer.
During the process of building the project you will be asked:
- For the path to the Eclipse workspace you want to configure. The one offered to us is usually ok.
- If we want to specify the path to gvSIG on which to deploy the plugin. Normally the path offered to us will be valid.
The script for creating our project, in addition to generating the source code and configuring Maven for use, compiles and displays the first version of the project in the Maven local repository, and also generates the corresponding Eclipse projects so that they can be imported into our workspace.
Project structure
Direct translation from Google translator
Once the wizard has generated our project we can get started by opening Eclipse and selecting as our workspace the folder that was indicated in the wizard. This opens an instance of Eclipse with an empty workspace. The first thing to do is to import the two projects that have been created. For this we will not use the import option.
From the File menu select New->Project... (not java project) and in the dialogue that appears select the General->Project option and click Next. Use "org.gvsig.viewer" as the project name to complete the process. Do the same for the "org.gvsig.viewer.app" project.
Tip
You may find it useful to consult information relating to this in the Developer Guide in the gvSIG project structure section and also in Things to consider before developing a plugin.
Let's take a look at the generated code. The projects that have been created are:
- org.gvsig.viewer. This is a Maven project with multiple subprojects. In these we find the logic part, with its API and implementation, the user interface (and associated API and implementation) associated with the logical components, and a project that allows us to test the logic and interfaces without having to start gvSIG. In general the various components that we find here will be independent of the Andami framework as well as the gvSIG plugins, being dependent only on other libraries. The projects that we will find are:
- org.gvsig.viewer.lib with the logic of our project
- org.gvsig.viewer.lib.api
- org.gvsig.viewer.lib.impl
- org.gvsig.viewer.swing with the user interface of our
logical components.
- org.gvsig.viewer.swing.api
- org.gvsig.viewer.swing.impl
- org.gvsig.viewer.main our main testing.
- org.gvsig.viewer.lib with the logic of our project
- org.gvsig.viewer.app This Maven project is also a multimodule project, that is it has sub-projects. These will contain the implementation of the various plugins that will be added to gvSIG. Normally you will use the components of the org.gvsig.viewer project and be responsible for the integration of these in gvSIG. In the template used for the generated sample project there is only one subproject, org.gvsig.viewer.app.mainplugin, since we will be providing a single plugin. If we had needed to contribute more than one plugin we would have a subproject for each plugin.
We could work with this but since Eclipse is not able to recognise the structure of Maven subprojects it will be much easier to import the child projects as Eclipse projects.
Select the File->import menu option and in the import dialog and select General->Existing Projects Into Workspace and click next. Click "Select root directory" and select the folder "/home/gvsig/workspce/viewer/org.gvsig.viewer/org.gvsig.viewer.lib". In doing this we suggest adding these projects:
- org.gvsig.viewer.lib.api
- org.gvsig.viewer.lib.impl
These will be selected so just press the button end.
Now repeat the process indicating as "root directory" the following folders:
- /home/gvsig/workspce/viewer/org.gvsig.viewer/org.gvsig.viewer.swing
- /home/gvsig/workspce/viewer/org.gvsig.viewer/org.gvsig.viewer.main
- /home/gvsig/workspce/viewer/org.gvsig.viewer.app/org.gvsig.viewer.app.mainplugin
We have now imported all the projects related to our development into our workspace.
Dependencies between projects
First, let's see what dependencies the assistant has left us with in the org.gvsig.viewer project. Notice the following entry in the dependencyManagement section in the project's pom.xml file:
<dependencies>
<dependency>
<groupId>org.gvsig</groupId>
<artifactId>org.gvsig.core.maven.dependencies</artifactId>
<version>2.0.1-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
This imports the dependencies of the various gvSIG libraries so that we don't have to worry about the versions of any of the gvSIG base libraries. As this importing is done in the dependencyManagement section, it is for information purposes only, ie a dependency on these libraries is not set.
If we now look at the dependencies section, we find the entry:
<dependency>
<groupId>org.gvsig</groupId>
<artifactId>org.gvsig.core.maven.dependencies</artifactId>
<version>2.0.1-SNAPSHOT</version>
<type>pom</type>
<scope>test</scope>
</dependency>
This sets the implementation and test dependencies for all the core libraries of gvSIG so that we won't have to worry about whether all the implementations of the gvSIG APIs are loaded when testing our projects.
In addition we see:
<dependency>
<groupId>org.gvsig</groupId>
<artifactId>org.gvsig.tools.lib</artifactId>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.gvsig</groupId>
<artifactId>org.gvsig.tools.lib</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.gvsig</groupId>
<artifactId>org.gvsig.fmap.geometry</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.gvsig</groupId>
<artifactId>org.gvsig.fmap.dal</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.gvsig</groupId>
<artifactId>org.gvsig.metadata.lib.basic.api</artifactId>
<scope>compile</scope>
</dependency>
These are fixed build dependencies with the main gvSIG libraries, and are usually required in almost any project that we make with gvSIG.
This configuration of dependencies is found in the main project. Let's now see what dependencies are required for the subprojects.
org.gvsig.viewer.lib/pom.xml. This does not provide any declarations of dependencies additional to those defining the parent project already discussed.
org.gvsig.viewer.lib.api/pom.xml. Neither of these provide new dependencies.
org.gvsig.viewer.lib.impl/pom.xml. Does not provide new dependencies, but in this project we should add dependencies to the API project, both for compilation and for execution of tests.
org.gvsig.viewer.swing/pom.xml. Although not providing new dependencies, defined as dependencies of the lib.api. projects.
org.gvsig.viewer.swing.api/pom.xml. Does not add new dependencies.
org.gvsig.viewer.swing.imple/pom.xml. Although not providing dependencies you must declare dependencies with the swing API for testing and building.
Warning
There is also has a dependency on the implementation for compilation and even if you don't think you need it, you should review it.
And finally org.gvsig.viewer.main/pom.xml. This will declare the dependencies with our libraries' API for compilation and implementation for execution. Also adds build dependencies to the gvSIG libraries it uses, these being:
<dependency> <groupId>org.gvsig</groupId> <artifactId>org.gvsig.fmap.control</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.gvsig</groupId> <artifactId>org.gvsig.fmap.dal</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.gvsig</groupId> <artifactId>org.gvsig.fmap.geometry</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.gvsig</groupId> <artifactId>org.gvsig.fmap.mapcontext</artifactId> <scope>compile</scope> </dependency>
The Logic Library
The logic part of our development is divided into two projects:
- org.gvsig.viewer.lib.api
- org.gvsig.viewer.lib.impl
In the API project we find mostly interfaces. These interfaces make up the API of the logic components to be developed. The class model of the example is shown in the following diagram:
We have the following entities:
VisorLibrary, which represents the object Library. In this note class methods:
doRegistration, which runs to load the library and tells what kind of library is, in our case the definition of an API as well as what other libraries are required initialized before this, the library of data access, and geometry. The code doing this is:
public void doRegistration() { this.registerAs(VisorLibrary.class, Library.TYPE.API); this.require(DALLibrary.class); this.require(GeometryLibrary.class); }
doPostInitialize, which is executed when all bookstores have been initialized. In our case checks to verify that at least one implementation API has been registered. The code that does this is:
protected void doPostInitialize() throws LibraryException { // Validate if there are any implementation registered. VisorManager manager = VisorLocator.getManager(); if (manager == null) { throw new ReferenceNotRegisteredException( VisorLocator.MANAGER_NAME, VisorLocator.getInstance() ); } }
VisorLocator, is the locator of our library, responsible for give us the instance of our manager. This is a class with methods static to register implementations of this API and an implementation him.
VisorManager. A interface. The manager of the bookstore. Defines methods to obtain the blocks and plots as well as locating an apple given a point. It is the entrance to the various entities in our model.
VisorBlock An interface that represents a block, and provides methods for geometry that defines the block or for the parcels are.
VisorProperty. An interface that represents a cadastral parcel. It has methods for geometry that defines it, your code or municipality.
Let's see what we are in the implementation part. Observe that while in the normal API interfaces has been found in the implementation we find classes. Classes that implement the various interfaces that defined in the API. By convention, the implementation of the various interfaces that appear in the API will call the API like prepending the prefix Default.
The class model of implementation for our example is:
Let us see the most relevant parts of the implementation.
VisorDefaultImplLibrary. As in the API, this class is responsible for initialize the library implementation. In this note class methods:
doRegistration, which is responsible for registering the library as an implementation API. VisorLibrary Should you have other dependencies other than those marked on the API, would be added here to make sure these libraries are initialized before this, but it is the case of our example. When you register this library as an implementation of VisorLibrary, cover two functions. On one side the library API will always initialized before this implementation, and secondly when some other library dependencies with the API set, the initialization mechanism library is responsible for initializing the implementation along with the API ensure you have an implementation of the API that we required. The code you need to put in our example is:
public void doRegistration() { this.registerAsImplementationOf(VisorLibrary.class); }
doInitialize, que se encarga de registrar en el locator del API la implementación del manager que tenemos en esta librería. El código que hace esto es:
protected void doInitialize() throws LibraryException { VisorLocator.registerManager(DefaultVisorManager.class); }
DefaultVisorManager. De esta clase conviene resaltar principalmente:
El método getBlock. Podemos ver cómo realizar una busqueda de un registro filtrando por una condición espacial:
try { String attrGeomName = blocks.getDefaultFeatureType().getDefaultGeometryAttributeName(); FeatureQuery query = blocks.createFeatureQuery(); query.setFilter( new IntersectsEvaluator(attrGeomName,point) ); set = blocks.getFeatureSet(query); if( set.isEmpty() ) { return null; } it = set.fastiterator(); Feature f = (Feature) it.next(); VisorBlock block = new DefaultVisorBlock(this,f.getGeometry(attrGeomName)); return block; } catch (DataException e) { ... } finally { if( it != null ) { it.dispose(); } if( set != null ) { set.dispose(); } }
Tip
You can find information on the mechanisms available for querying the data access Guide to the developer of the Data Access Library (DAL)
From this point out:
- We can find out which attribute of the features is the one that contains the geometry through the getDefaultGeometryAttributeName from feature type of our store.
- To perform a search for features in a store we will invoke the method Passed as parameter getFeatureSet an instance of FeatureQuery in which specify the filter conditions. It can also be specified order or attributes that we want to recover in our query.
- The filter condition is specified by providing a method setFilter Evaluator of our query.
- We take care of releasing objects that we, like iterators o feature sets. Keep in mind that depending on the type of * store * with which we are working they may have reserved resources and connections to databases, recordsets or connections remote servers.
The openShape method. Here's how to open a store based on existing shapes:
parameters = manager.createStoreParameters("Shape"); parameters.setDynValue("shpfile", shape); parameters.setDynValue("crs", "EPSG:23030"); return (FeatureStore) manager.openStore("Shape", parameters);
Note that, to open a store we will do this in two phases. On the one hand create a structure to house the parameters needed to open our store the initialized with the appropriate values and invoke the openStore method from the data access manager within those parameters.
Each type of store will have a set of parameters specific to it. To open a shape, we indicate at least the name of the file and the reference system in which is located.
IntersectsEvaluator. This is the class that evaluates the condition used in the filter. This field class checks if the specified geometry intersects a given feature specific geometry. In its construction will provide the field containing the feature geometry and geometry to check whether there are intersecting. In this class should be emphasized:
The evaluate method, responsible for conducting the test:
Geometry op1geom = (Geometry) data.getDataValue(this.op2attrname); return new Boolean(this.op1geom.intersects(op1geom));
Knowing how to call the attribute that contains the geometry we can get this through the getDataValue method. Once we have the two geometries intersecs invoke * method * of the geometry to see if intersect.
The getCQL method. getCQL This method returns a string in the format of a where of sql to use as a filter in stores that attack to the database sql. The filter can be returned not be exactly the same as that implemented by the method code Evaluate, acting as a filter prior to this if the store supports it.
DefaultVisorBlock. Represents a block from our domain. Stores geometry that gives shape to the block. The most important of this class is the getProperties method that returns all parcels that are on the block:
List<VisorProperty> properties = new ArrayList<VisorProperty>(); FeatureStore store = this.manager.getProperties(); String attrGeomName = store.getDefaultFeatureType().getDefaultGeometryAttributeName(); FeatureQuery query = store.createFeatureQuery(); query.setFilter( new IntersectsEvaluator(attrGeomName, this.shape) ); set = this.manager.getProperties().getFeatureSet(query); if( set.isEmpty() ) { return null; } it = set.fastiterator(); while( it.hasNext() ) { Feature f = (Feature) it.next(); VisorProperty property = new DefaultVisorProperty( this.manager, f.getString(PROPERTIES_CODE), f.getGeometry(attrGeomName), f.getInt(PROPERTIES_CREATIONDATE), f.getInt(PROPERTIES_MUNICODE) ); properties.add(property); } return properties;
We can see that the same mechanism used to filter the plots use the manager to retrieve an apple. In this case once you get the set the plots, runs, retrieving data from these and creating plot objects.
DefaultVisorProperty. This is the class that represents a plot. In our example is not only logical, merely exposing the data store by geters.
Presentation libray
Direct translation with Google Translator
As happened with the logic, the presentation also will be divided into two projects, on the one hand and on the other API implementation.
- org.gvsig.visor.swing.api
- org.gvsig.visor.swing.impl
Similar to as with the logic in the presentation, in the draft We only have the API interfaces and abstract classes that define our API. the API of the presenting part is formed by the interface along with manager a set of abstract classes that define the public API of our components, components usually extend to swing JPanel component. These classes are interfaces and abstract because no swing model interfaces for its components. In our example, the only component that is we visual component associated with an apple, the JVisorBlockPanel, which extends from JPanel API level by adding a single method that allows us to obtain the VisorBlock logical component that is associated at any given time.
In the implementation we will find the class DefaultJVisorBlockPanel it receives in its constructor the instance of the VisorBlock to be submitted their data. In general the presentation is not a complication beyond of persons as may be handling swing. The only thing to note is that the presentation should not use anything that is not exposed in the API from our library of logic.
A test application
Direct translation fron google translator
So far we've seen pieces that made up the various loose components that we needed to do our small viewfinder. We will see Now how can we join all those pieces and some more to create a simple viewer let us try everything without having to boot all gvSIG.
To do this, we focus on the org.gvsig.visor.main project. It can find a main class with everything you need to present our map and test our components.
Our program presents a window with a map and a few buttons to zoom or pan tools enable or our information tool on parcels of an apple.
Tip
It may be useful to consult the existing literature on the component MapControl This documentation is not has been updated with the changes that have made in that component in version 2 of gvSIG broad lines but continues to provide the same functionality and help better understand the tool.
To do this we will:
Create our swing component to display a map:
mapControlManager = MapControlLocator.getMapControlManager();
mapControl = mapControlManager.createJMapControlPanel();
We add the standard tools of zoom and pan, and we will set pan tool as the active tool:
mapControl.addBehavior( "zoom", new Behavior[] { new RectangleBehavior(new ZoomInListenerImpl(mapControl)), new PointBehavior(new ZoomOutRightButtonListener(mapControl)) } ); mapControl.addBehavior( "pan", new MoveBehavior( new PanListenerImpl(mapControl) ) ); mapControl.setTool("pan");
Add the buttons and links to tools that just register on our map:
toolBar.add( new JButton( new AbstractAction("Zoom") { public void actionPerformed(ActionEvent e) { mapControl.setTool("zoom"); } } ) );
We will create our layer of apples and add to the map:
FLyrVect layer = (FLyrVect) MapContextLocator.getMapContextManager().createLayer( "Blocks", manager.getBlocks() ); mapControl.getMapContext().getLayers().addLayer(layer);
And finally we will create our own tools and will record information on the map:
PropertiesOfBlockListener listener = new PropertiesOfBlockListener(); mapControl.addBehavior(SHOWINFO_TOOL_NAME, new PointBehavior(listener));
This will register a point-like behavior, which associates a listener to perform particular actions that do not interest you. The listener code would be:
public class PropertiesOfBlockListener extends AbstractPointListener { public void point(PointEvent event) throws BehaviorException { VisorSwingManager swingManager = VisorSwingLocator.getSwingManager(); VisorBlock block; try { block = swingManager.getManager().getBlock(event.getMapPoint()); if( block == null ) { return; } JPanel panel = swingManager.createJVisorBlockPanel(block); ToolsLocator.getWindowManager().showWindow(panel, "Block information", WindowManager.MODE.TOOL); } catch (VisorException e) { // FIXME: Process exception throw new RuntimeException("Can't show properties of selected block.",e); } } }
Primarily derived from AbstractPointListener overwriting the point method that is called to perform the action associated with this tool. This method is limited to call our getBlocks method from the position on the map that was clicked, and if get some apple at that point our panel will be created to report this information in a window.
What might be more remarkable when viewed in the parameter event for the coordinates of the point that was clicked is done through the getMapPoint method to ensure that returns us to map coordinates and not screen.
Integrating with gvSIG
Direct translation from google Translator
So far we have seen how to create our components, logic and user interface, using gvSIG libraries to access the geographic data or to present as well as a simple way to create a small application that uses them. Let's see now as integrate implementing these features in gvSIG.
If we look at the projects we have in your workspace you will see that there is still one on which we have not worked, org.gvsig.visor.app.mainplugin . That is where we implemented plugin. Before the plugin code to see a detailed comment. When we described what had to do our extension, we said we had to present a splash customized. Let us first how we can do this.
In the folder "src/main/resources" folder find a theme, and within this one andami-theme.xml file. This file is responsible for specifying the andami framework which splash must dislay as well as must be used a background image in the application MDI or the gvSIG windows icons. Andami, when starts, searchs into the extensions folder anyone wich contains a theme folder and within this file and when it finds it, it uses it. Xml file in our example contains:
<AndamiProperties>
<ApplicationImages>
<SplashImages>
<Splash
path="splash.png"
timer="10000"
x="270" y="240"
fontsize="18"
color="80,170,240"
version="2.0"/>
</SplashImages>
<!--BackgroundImage path="theme/logo_es.png"/-->
<!--WallpaperType value="CENTERED"/-->
<Icon path="$GVSIG_INSTALL/theme/icon.png"/>
</ApplicationImages>
<ApplicationName value="gvSIG 2.0.0"/>
</AndamiProperties>
By default routes appear in the file will be interpreted on the location of this file, having a variable GVSIG_INSTALL point to the folder where gvSIG is installed. In the example we see how the tag Splash not indicated path to the file "splash.png" Authors used the files in the folder while the plugin tag Icon variable GVSIG_INSTALL is used to refer the file is in the default theme of Andami.
GvSIG can start and check out the splash indicated in our andami-theme.xml.
Having seen how we can change the splash, we take a look at the extension of our plugin.
Now let's see the code for our plugin. We will see that there are only two classes, VisorExtension and PropertiesOfBlockListener VisorExtension. The class that integrates our functionality in gvSIG is VisorExtension. This class extends the Andami class Extension to integrate with the menus and toolbars, and implements the ExclusiveUIExtension interface to control the visibility of other gvSIG extensions.
To control the visibility of the other areas of gvSIG, the interface ExclusiveUIExtension provides methods:
isEnabled receiving as parameter the extension on which want to know whether to be enabled or not:
public boolean isEnabled(IExtension extension)
In our case, as we want to disable all extensions editing, check if the extension that comes in the java package "org.gvsig.editing" and for all who are in that package return false, while for the rest, delegating them to determine whether or not to be enabled.
isVisible receiving as parameter the extension on which want to find out whether it should be visible or not:
public boolean isVisible(IExtension extension)
In our case, we will use to determine which areas should be visible the same we use to determine whether they should be disabled.
It is very important delegate isEnabled or isVisible methods of each extension and not return true as you may specify the extent of certain specified conditions; to be visible and active checks herself.
The methods that we find in our class VisorExtension that needs to extend to Extension are:
initialize. Is invoked when loading a. Here we will simply record services provided by our extension. In our case we will simply inform the plugins Manager that our viewer class wants our to act as controller of the visibility of all extensions. This is done through the method SetExclusiveUIExtension:
PluginsManager manager = PluginsLocator.getManager(); manager.setExclusiveUIExtension(this);
PostInitialize. Invoked during the initialization of the plugin, once invoked initialize method of all extensions. This guarantees that when you run will become available virtually all parts of gvSIG. Take advantage of this method for:
Add our information about gvSIG, this is done through our addToAbout function. The code that does is:
AboutManager about = AboutLocator.getManager(); URL description = getResourceURL("about/description.html"); URL icon = getResourceURL("about/icon.png"); AboutParticipant dev = about.addDeveloper("Mi empresa", description, 1, icon); dev.addContribution("Mi visor", "Visor para consulta de parcelas catastrales", 2011, 5, 1, 2011, 7, 1);
When we add our information to "about", we have to do two things:
Add an entry and developers with our information. We provide the name of our company and a description of this by the URL to an HTML document. Normally this document is resources of our project.
If two extensions try to add more than once as a business developer with the same name will be added only the first.
Added information about our company development we have done, a name and a brief description.
Normally we make each plugin register the company, such information concrete development of the plugin.
Create the manager of the logic part of our library.
Initialize stores through our function initializeStores. As the manager of our logical library has methods available to initialize the stores, we will be limited to invoke them:
manager.initialize( getResource("data/properties.shp"), getResource("data/blocks.shp") );
And finally create and display for sale with our eyes, through the method createViewWindow. let's see a little more detail how this is done. Before you start first thing to do is to obtain an object reference application and projects Manager:
ApplicationManager application = ApplicationLocator.getManager(); ProjectManager projectManager = application.getProjectManager();
Once we have these references, we can create our view:
// 1. Create a new view and set the name. ViewManager viewManager = (ViewManager) projectManager.getDocumentManagers(ViewManager.TYPENAME); ViewDocument view = (ViewDocument) viewManager.createDocument(); view.setName(MY_VIEW_NAME);
To create the view, ask to ProjectManager to return the manager views as This will ask a new instance of the document view. We recall here one of the main features of the manager is to act as a factory for instances of the managed objects that manager. Once we have the document view, assign the name that we deem appropriate.
With the view already created, we will see what we do to add to this the layers need it. This will create a layer with the Manzamo this end we use createLayer method, indicating the name of the layer and the store on that we want to be based:
// 2. Create a new layer with the blocks FLyrVect layer = (FLyrVect) application.getMapContextManager().createLayer("Blocks", this.manager.getBlocks());
With the layer already created, add the * map * of the view the new layer:
// 3. Add this layer to the mapcontext of the new view. view.getMapContext().getLayers().addLayer(layer);
We will add th view to the current project:
// 4. Add the view to the current project. projectManager.getCurrentProject().add(view);
And finally we will present the window Join the view that just created:
// 5. Force to show the view's window. IView viewWindow = (IView) viewManager.getMainWindow(view); application.getUIManager().addWindow(viewWindow, GridBagConstraints.CENTER); try { application.getUIManager().setMaximum((IWindow) viewWindow, true); } catch (PropertyVetoException e) { logger.info("Can't maximize view.",e); }
Once we have shown our view window, be recorded in the map graphic component we bring the new tool, similar to as we did to add it to our map in the test project:
// 6. Register my tool in the mapcontrol of the view. PropertiesOfBlockListener listener = new PropertiesOfBlockListener(); viewWindow.getMapControl().addBehavior(TOOL_NAME, new PointBehavior(listener));
With all this we initialize our plugin.
execute. This method will be invoked whenever the user interacts with the options menu or buttons that are configured in the config.xml file of our plugin. In our case, was set to trigger this event when the user wanted to activate the tool of information on cadastral parcels an apple. So the code would have to have there must correspond with this:
if( ACTION_SETINFOTOOL.equalsIgnoreCase(actionCommand) ) { // Set the tool in the mapcontrol of the active view. ApplicationManager application = ApplicationLocator.getManager(); if( application.getActiveWindow() != viewWindow ) { return; } viewWindow.getMapControl().setTool(TOOL_NAME); } }
Observe that the first thing we do is check if the command received is that corresponding to the activation of our tool, and we set in config.xml. This is because in the same extension can group several tools, indicating different command names in the config.xml for each.
Once we know that you are trying to activate our reporting tool, check if the window is active in our view, since on other views or other document types, we should not do anything. And finally, we will work to activate our tool in the map view. Toolmakers we recorded postInitialize * in * our extension.
isVisible. In this method we report if the menus and buttons associated with our tool should be visible. In our case, our tool will leave visible where is the active window from view:
ApplicationManager application = ApplicationLocator.getManager(); return application.getActiveWindow() == viewWindow;
isEnabled, which always return 'true', since our tool is active whenever it is visible, and there he put the necessary checks. If the logic allows our tool is not active in some cases which itself is allowed to be visible, will be here where we make these checks.
Basically, we have reviewed as is the integration of our functionality in gvSIG. It remains to be seen PropertiesOfBlockListener class we use to create our tool. The code of the listener is basically similar to that used by our test application.
Distributing our project.
direct translation from google translator*
Once we have our development can be deployed on a run that gvSIG gvSIG and see if it works. Now, normally our work not end there. Typically, we have to do to get these plugins to our users, or even before testers to verify that everything works properly.
With version 2.0 of gvSIG we developed a packaging system that allow us to distribute plugins so that users can install easily from the Add-ons Manager gvSIG (this functionality was ported a gvSIG version 1.11.0).
Tip
You can consult the documentation on how to build packages plugin for gvSIG in the section of the development guide, Generate an installation package in paragraph Generation from installation plugin .
The point is how to generate these packages with our plugins. Like with the standard installation of gvSIG had a utility to assist in the creation of our development projects, there is also a utility to generate a plugin packages already installed on gvSIG.
As we had to activate the project creation wizard from the preferences window, activate the same way this utility. We'll go gvSIG preferences panel, and in general, extensions look "org.gvsig.installer.app.extension.creation.MakePluginPackageExtension", and activate the extension. Once activated we gvSIG restart to take effect changes.
When you reboot gvSIG have the option "Create plugin installation package" in the menu Tools->Development. We will use this tool to build the package Installation of our plugin.
The installation package can be generated directly distribute to our users or to have them reach gvSIG to be exposed in the repository gvSIG supplements and available to users directly from the Add-ons Manager through of the URL displayed by default.
Likewise, for when it is ready the final version of gvSIG 2.0 is expected to have a mechanism that allows to easily include these binary packages in the official distribution gvSIG so that we can have a custom installer for our plugins deliver to our customers.